parent
							
								
									aaf6f88c9c
								
							
						
					
					
						commit
						c702b9421a
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -16,5 +16,3 @@ deps/tools/setuidgids | |||
| *.sw? | ||||
| *.debug | ||||
| stage2/01-sys-tweaks/extfiles | ||||
| deps/examples/cpp-multiCameraServer/main.cpp | ||||
| deps/examples/java-multiCameraServer/src | ||||
|  |  | |||
							
								
								
									
										3
									
								
								deps/04-copy.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								deps/04-copy.sh
									
									
									
									
										vendored
									
									
								
							|  | @ -7,9 +7,6 @@ mkdir -p ${DEST} | |||
| # | ||||
| # examples | ||||
| # | ||||
| mkdir -p examples/java-multiCameraServer/src/main/java | ||||
| cp allwpilib/cameraserver/multiCameraServer/src/main/java/Main.java examples/java-multiCameraServer/src/main/java/ | ||||
| cp allwpilib/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp examples/cpp-multiCameraServer/ | ||||
| 
 | ||||
| sh -c 'cd examples && zip -r - java-multiCameraServer' > ${DEST}/java-multiCameraServer.zip | ||||
| sh -c 'cd examples && zip -r - cpp-multiCameraServer' > ${DEST}/cpp-multiCameraServer.zip | ||||
|  |  | |||
							
								
								
									
										224
									
								
								deps/examples/cpp-multiCameraServer/main.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								deps/examples/cpp-multiCameraServer/main.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,224 @@ | |||
| /*----------------------------------------------------------------------------*/ | ||||
| /* Copyright (c) 2018 FIRST. All Rights Reserved.                             */ | ||||
| /* Open Source Software - may be modified and shared by FRC teams. The code   */ | ||||
| /* must be accompanied by the FIRST BSD license file in the root directory of */ | ||||
| /* the project.                                                               */ | ||||
| /*----------------------------------------------------------------------------*/ | ||||
| 
 | ||||
| #include <cstdio> | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <networktables/NetworkTableInstance.h> | ||||
| #include <vision/VisionPipeline.h> | ||||
| #include <vision/VisionRunner.h> | ||||
| #include <wpi/StringRef.h> | ||||
| #include <wpi/json.h> | ||||
| #include <wpi/raw_istream.h> | ||||
| #include <wpi/raw_ostream.h> | ||||
| 
 | ||||
| #include "cameraserver/CameraServer.h" | ||||
| 
 | ||||
| /*
 | ||||
|    JSON format: | ||||
|    { | ||||
|        "team": <team number>, | ||||
|        "ntmode": <"client" or "server", "client" if unspecified> | ||||
|        "cameras": [ | ||||
|            { | ||||
|                "name": <camera name> | ||||
|                "path": <path, e.g. "/dev/video0"> | ||||
|                "pixel format": <"MJPEG", "YUYV", etc>   // optional
 | ||||
|                "width": <video mode width>              // optional
 | ||||
|                "height": <video mode height>            // optional
 | ||||
|                "fps": <video mode fps>                  // optional
 | ||||
|                "brightness": <percentage brightness>    // optional
 | ||||
|                "white balance": <"auto", "hold", value> // optional
 | ||||
|                "exposure": <"auto", "hold", value>      // optional
 | ||||
|                "properties": [                          // optional
 | ||||
|                    { | ||||
|                        "name": <property name> | ||||
|                        "value": <property value> | ||||
|                    } | ||||
|                ] | ||||
|            } | ||||
|        ] | ||||
|    } | ||||
|  */ | ||||
| 
 | ||||
| #ifdef __RASPBIAN__ | ||||
| static const char* configFile = "/boot/frc.json"; | ||||
| #else | ||||
| static const char* configFile = "frc.json"; | ||||
| #endif | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| unsigned int team; | ||||
| bool server = false; | ||||
| 
 | ||||
| struct CameraConfig { | ||||
|   std::string name; | ||||
|   std::string path; | ||||
|   wpi::json config; | ||||
| }; | ||||
| 
 | ||||
| std::vector<CameraConfig> cameraConfigs; | ||||
| 
 | ||||
| wpi::raw_ostream& ParseError() { | ||||
|   return wpi::errs() << "config error in '" << configFile << "': "; | ||||
| } | ||||
| 
 | ||||
| bool ReadCameraConfig(const wpi::json& config) { | ||||
|   CameraConfig c; | ||||
| 
 | ||||
|   // name
 | ||||
|   try { | ||||
|     c.name = config.at("name").get<std::string>(); | ||||
|   } catch (const wpi::json::exception& e) { | ||||
|     ParseError() << "could not read camera name: " << e.what() << '\n'; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // path
 | ||||
|   try { | ||||
|     c.path = config.at("path").get<std::string>(); | ||||
|   } catch (const wpi::json::exception& e) { | ||||
|     ParseError() << "camera '" << c.name | ||||
|                  << "': could not read path: " << e.what() << '\n'; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   c.config = config; | ||||
| 
 | ||||
|   cameraConfigs.emplace_back(std::move(c)); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool ReadConfig() { | ||||
|   // open config file
 | ||||
|   std::error_code ec; | ||||
|   wpi::raw_fd_istream is(configFile, ec); | ||||
|   if (ec) { | ||||
|     wpi::errs() << "could not open '" << configFile << "': " << ec.message() | ||||
|                 << '\n'; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // parse file
 | ||||
|   wpi::json j; | ||||
|   try { | ||||
|     j = wpi::json::parse(is); | ||||
|   } catch (const wpi::json::parse_error& e) { | ||||
|     ParseError() << "byte " << e.byte << ": " << e.what() << '\n'; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // top level must be an object
 | ||||
|   if (!j.is_object()) { | ||||
|     ParseError() << "must be JSON object\n"; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // team number
 | ||||
|   try { | ||||
|     team = j.at("team").get<unsigned int>(); | ||||
|   } catch (const wpi::json::exception& e) { | ||||
|     ParseError() << "could not read team number: " << e.what() << '\n'; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // ntmode (optional)
 | ||||
|   if (j.count("ntmode") != 0) { | ||||
|     try { | ||||
|       auto str = j.at("ntmode").get<std::string>(); | ||||
|       wpi::StringRef s(str); | ||||
|       if (s.equals_lower("client")) { | ||||
|         server = false; | ||||
|       } else if (s.equals_lower("server")) { | ||||
|         server = true; | ||||
|       } else { | ||||
|         ParseError() << "could not understand ntmode value '" << str << "'\n"; | ||||
|       } | ||||
|     } catch (const wpi::json::exception& e) { | ||||
|       ParseError() << "could not read ntmode: " << e.what() << '\n'; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // cameras
 | ||||
|   try { | ||||
|     for (auto&& camera : j.at("cameras")) { | ||||
|       if (!ReadCameraConfig(camera)) return false; | ||||
|     } | ||||
|   } catch (const wpi::json::exception& e) { | ||||
|     ParseError() << "could not read cameras: " << e.what() << '\n'; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| cs::UsbCamera StartCamera(const CameraConfig& config) { | ||||
|   wpi::outs() << "Starting camera '" << config.name << "' on " << config.path | ||||
|               << '\n'; | ||||
|   auto camera = frc::CameraServer::GetInstance()->StartAutomaticCapture( | ||||
|       config.name, config.path); | ||||
| 
 | ||||
|   camera.SetConfigJson(config.config); | ||||
| 
 | ||||
|   return camera; | ||||
| } | ||||
| 
 | ||||
| // example pipeline
 | ||||
| class MyPipeline : public frc::VisionPipeline { | ||||
|  public: | ||||
|   int val = 0; | ||||
| 
 | ||||
|   void Process(cv::Mat& mat) override { | ||||
|     ++val; | ||||
|   } | ||||
| }; | ||||
| }  // namespace
 | ||||
| 
 | ||||
| int main(int argc, char* argv[]) { | ||||
|   if (argc >= 2) configFile = argv[1]; | ||||
| 
 | ||||
|   // read configuration
 | ||||
|   if (!ReadConfig()) return EXIT_FAILURE; | ||||
| 
 | ||||
|   // start NetworkTables
 | ||||
|   auto ntinst = nt::NetworkTableInstance::GetDefault(); | ||||
|   if (server) { | ||||
|     wpi::outs() << "Setting up NetworkTables server\n"; | ||||
|     ntinst.StartServer(); | ||||
|   } else { | ||||
|     wpi::outs() << "Setting up NetworkTables client for team " << team << '\n'; | ||||
|     ntinst.StartClientTeam(team); | ||||
|   } | ||||
| 
 | ||||
|   // start cameras
 | ||||
|   std::vector<cs::VideoSource> cameras; | ||||
|   for (auto&& cameraConfig : cameraConfigs) | ||||
|     cameras.emplace_back(StartCamera(cameraConfig)); | ||||
| 
 | ||||
|   // start image processing on camera 0 if present
 | ||||
|   if (cameras.size() >= 1) { | ||||
|     std::thread([&] { | ||||
|       frc::VisionRunner<MyPipeline> runner(cameras[0], new MyPipeline(), | ||||
|                                            [&](MyPipeline &pipeline) { | ||||
|         // do something with pipeline results
 | ||||
|       }); | ||||
|       /* something like this for GRIP:
 | ||||
|       frc::VisionRunner<MyPipeline> runner(cameras[0], new grip::GripPipeline(), | ||||
|                                            [&](grip::GripPipeline& pipeline) { | ||||
|         ... | ||||
|       }); | ||||
|        */ | ||||
|       runner.RunForever(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   // loop forever
 | ||||
|   for (;;) std::this_thread::sleep_for(std::chrono::seconds(10)); | ||||
| } | ||||
							
								
								
									
										245
									
								
								deps/examples/java-multiCameraServer/src/main/java/Main.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								deps/examples/java-multiCameraServer/src/main/java/Main.java
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,245 @@ | |||
| /*----------------------------------------------------------------------------*/ | ||||
| /* Copyright (c) 2018 FIRST. All Rights Reserved.                             */ | ||||
| /* Open Source Software - may be modified and shared by FRC teams. The code   */ | ||||
| /* must be accompanied by the FIRST BSD license file in the root directory of */ | ||||
| /* the project.                                                               */ | ||||
| /*----------------------------------------------------------------------------*/ | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.GsonBuilder; | ||||
| import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonParser; | ||||
| 
 | ||||
| import edu.wpi.cscore.VideoSource; | ||||
| import edu.wpi.first.cameraserver.CameraServer; | ||||
| import edu.wpi.first.networktables.NetworkTableInstance; | ||||
| import edu.wpi.first.vision.VisionPipeline; | ||||
| import edu.wpi.first.vision.VisionThread; | ||||
| 
 | ||||
| import org.opencv.core.Mat; | ||||
| 
 | ||||
| /* | ||||
|    JSON format: | ||||
|    { | ||||
|        "team": <team number>, | ||||
|        "ntmode": <"client" or "server", "client" if unspecified> | ||||
|        "cameras": [ | ||||
|            { | ||||
|                "name": <camera name> | ||||
|                "path": <path, e.g. "/dev/video0"> | ||||
|                "pixel format": <"MJPEG", "YUYV", etc>   // optional | ||||
|                "width": <video mode width>              // optional | ||||
|                "height": <video mode height>            // optional | ||||
|                "fps": <video mode fps>                  // optional | ||||
|                "brightness": <percentage brightness>    // optional | ||||
|                "white balance": <"auto", "hold", value> // optional | ||||
|                "exposure": <"auto", "hold", value>      // optional | ||||
|                "properties": [                          // optional | ||||
|                    { | ||||
|                        "name": <property name> | ||||
|                        "value": <property value> | ||||
|                    } | ||||
|                ] | ||||
|            } | ||||
|        ] | ||||
|    } | ||||
|  */ | ||||
| 
 | ||||
| public final class Main { | ||||
|   private static String configFile = "/boot/frc.json"; | ||||
| 
 | ||||
|   @SuppressWarnings("MemberName") | ||||
|   public static class CameraConfig { | ||||
|     public String name; | ||||
|     public String path; | ||||
|     public JsonObject config; | ||||
|   } | ||||
| 
 | ||||
|   public static int team; | ||||
|   public static boolean server; | ||||
|   public static List<CameraConfig> cameraConfigs = new ArrayList<>(); | ||||
| 
 | ||||
|   private Main() { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Report parse error. | ||||
|    */ | ||||
|   public static void parseError(String str) { | ||||
|     System.err.println("config error in '" + configFile + "': " + str); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Read single camera configuration. | ||||
|    */ | ||||
|   public static boolean readCameraConfig(JsonObject config) { | ||||
|     CameraConfig cam = new CameraConfig(); | ||||
| 
 | ||||
|     // name | ||||
|     JsonElement nameElement = config.get("name"); | ||||
|     if (nameElement == null) { | ||||
|       parseError("could not read camera name"); | ||||
|       return false; | ||||
|     } | ||||
|     cam.name = nameElement.getAsString(); | ||||
| 
 | ||||
|     // path | ||||
|     JsonElement pathElement = config.get("path"); | ||||
|     if (pathElement == null) { | ||||
|       parseError("camera '" + cam.name + "': could not read path"); | ||||
|       return false; | ||||
|     } | ||||
|     cam.path = pathElement.getAsString(); | ||||
| 
 | ||||
|     cam.config = config; | ||||
| 
 | ||||
|     cameraConfigs.add(cam); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Read configuration file. | ||||
|    */ | ||||
|   @SuppressWarnings("PMD.CyclomaticComplexity") | ||||
|   public static boolean readConfig() { | ||||
|     // parse file | ||||
|     JsonElement top; | ||||
|     try { | ||||
|       top = new JsonParser().parse(Files.newBufferedReader(Paths.get(configFile))); | ||||
|     } catch (IOException ex) { | ||||
|       System.err.println("could not open '" + configFile + "': " + ex); | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     // top level must be an object | ||||
|     if (!top.isJsonObject()) { | ||||
|       parseError("must be JSON object"); | ||||
|       return false; | ||||
|     } | ||||
|     JsonObject obj = top.getAsJsonObject(); | ||||
| 
 | ||||
|     // team number | ||||
|     JsonElement teamElement = obj.get("team"); | ||||
|     if (teamElement == null) { | ||||
|       parseError("could not read team number"); | ||||
|       return false; | ||||
|     } | ||||
|     team = teamElement.getAsInt(); | ||||
| 
 | ||||
|     // ntmode (optional) | ||||
|     if (obj.has("ntmode")) { | ||||
|       String str = obj.get("ntmode").getAsString(); | ||||
|       if ("client".equalsIgnoreCase(str)) { | ||||
|         server = false; | ||||
|       } else if ("server".equalsIgnoreCase(str)) { | ||||
|         server = true; | ||||
|       } else { | ||||
|         parseError("could not understand ntmode value '" + str + "'"); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // cameras | ||||
|     JsonElement camerasElement = obj.get("cameras"); | ||||
|     if (camerasElement == null) { | ||||
|       parseError("could not read cameras"); | ||||
|       return false; | ||||
|     } | ||||
|     JsonArray cameras = camerasElement.getAsJsonArray(); | ||||
|     for (JsonElement camera : cameras) { | ||||
|       if (!readCameraConfig(camera.getAsJsonObject())) { | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Start running the camera. | ||||
|    */ | ||||
|   public static VideoSource startCamera(CameraConfig config) { | ||||
|     System.out.println("Starting camera '" + config.name + "' on " + config.path); | ||||
|     VideoSource camera = CameraServer.getInstance().startAutomaticCapture( | ||||
|         config.name, config.path); | ||||
| 
 | ||||
|     Gson gson = new GsonBuilder().create(); | ||||
| 
 | ||||
|     camera.setConfigJson(gson.toJson(config.config)); | ||||
| 
 | ||||
|     return camera; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Example pipeline. | ||||
|    */ | ||||
|   public static class MyPipeline implements VisionPipeline { | ||||
|     public int val; | ||||
| 
 | ||||
|     @Override | ||||
|     public void process(Mat mat) { | ||||
|       val += 1; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Main. | ||||
|    */ | ||||
|   public static void main(String... args) { | ||||
|     if (args.length > 0) { | ||||
|       configFile = args[0]; | ||||
|     } | ||||
| 
 | ||||
|     // read configuration | ||||
|     if (!readConfig()) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // start NetworkTables | ||||
|     NetworkTableInstance ntinst = NetworkTableInstance.getDefault(); | ||||
|     if (server) { | ||||
|       System.out.println("Setting up NetworkTables server"); | ||||
|       ntinst.startServer(); | ||||
|     } else { | ||||
|       System.out.println("Setting up NetworkTables client for team " + team); | ||||
|       ntinst.startClientTeam(team); | ||||
|     } | ||||
| 
 | ||||
|     // start cameras | ||||
|     List<VideoSource> cameras = new ArrayList<>(); | ||||
|     for (CameraConfig cameraConfig : cameraConfigs) { | ||||
|       cameras.add(startCamera(cameraConfig)); | ||||
|     } | ||||
| 
 | ||||
|     // start image processing on camera 0 if present | ||||
|     if (cameras.size() >= 1) { | ||||
|       VisionThread visionThread = new VisionThread(cameras.get(0), | ||||
|               new MyPipeline(), pipeline -> { | ||||
|         // do something with pipeline results | ||||
|       }); | ||||
|       /* something like this for GRIP: | ||||
|       VisionThread visionThread = new VisionThread(cameras.get(0), | ||||
|               new GripPipeline(), pipeline -> { | ||||
|         ... | ||||
|       }); | ||||
|        */ | ||||
|       visionThread.start(); | ||||
|     } | ||||
| 
 | ||||
|     // loop forever | ||||
|     for (;;) { | ||||
|       try { | ||||
|         Thread.sleep(10000); | ||||
|       } catch (InterruptedException ex) { | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -44,7 +44,7 @@ class CameraConfig: pass | |||
| 
 | ||||
| team = None | ||||
| server = False | ||||
| cameras = [] | ||||
| cameraConfigs = [] | ||||
| 
 | ||||
| """Report parse error.""" | ||||
| def parseError(str): | ||||
|  | @ -70,7 +70,7 @@ def readCameraConfig(config): | |||
| 
 | ||||
|     cam.config = config | ||||
| 
 | ||||
|     cameras.append(cam) | ||||
|     cameraConfigs.append(cam) | ||||
|     return True | ||||
| 
 | ||||
| """Read configuration file.""" | ||||
|  | @ -128,6 +128,8 @@ def startCamera(config): | |||
| 
 | ||||
|     camera.setConfigJson(json.dumps(config.config)) | ||||
| 
 | ||||
|     return camera | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     if len(sys.argv) >= 2: | ||||
|         configFile = sys.argv[1] | ||||
|  | @ -146,8 +148,9 @@ if __name__ == "__main__": | |||
|         ntinst.startClientTeam(team) | ||||
| 
 | ||||
|     # start cameras | ||||
|     for camera in cameras: | ||||
|         startCamera(camera) | ||||
|     cameras = [] | ||||
|     for cameraConfig in cameraConfigs: | ||||
|         cameras.append(startCamera(cameraConfig)) | ||||
| 
 | ||||
|     # loop forever | ||||
|     while True: | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user