diff --git a/Software/LMSourceCode/ImageProcessing/cam2_thread.cpp b/Software/LMSourceCode/ImageProcessing/cam2_thread.cpp index 13f6736e..15227a59 100644 --- a/Software/LMSourceCode/ImageProcessing/cam2_thread.cpp +++ b/Software/LMSourceCode/ImageProcessing/cam2_thread.cpp @@ -65,7 +65,8 @@ bool Camera2Thread::init_pipeline() { options->Set().info_text = ""; const CameraHardware::CameraModel camera_model = GolfSimCamera::kSystemSlot2CameraType; - if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono) { + if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono || + camera_model != CameraHardware::CameraModel::Mira220_Mono) { options->Set().denoise = "cdn_off"; } else { options->Set().denoise = "auto"; diff --git a/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp b/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp index dbf930ca..993ca5d2 100644 --- a/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp +++ b/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp @@ -88,6 +88,7 @@ namespace golf_sim { { "3", CameraModel::PiHQ }, { "4", CameraModel::PiGS }, { "5", CameraModel::InnoMakerIMX296GS_Mono }, + { "6", CameraModel::Mira220_Mono }, { "100", CameraModel::kCameraUnknown }, }; if (camera_table.count(model_enum_value_string) == 0) @@ -133,7 +134,7 @@ namespace golf_sim { bool CameraHardware::camera_is_mono() const { - return (camera_model_ == CameraModel::InnoMakerIMX296GS_Mono); + return (camera_model_ == CameraModel::InnoMakerIMX296GS_Mono || camera_model_ == CameraModel::Mira220_Mono); } @@ -236,17 +237,17 @@ namespace golf_sim { } // This section deals with the common characteristics of some of the cameras - if (model == PiGS || + if (model == PiGS || model == InnoMakerIMX296GS_Mono) { - GS_LOG_TRACE_MSG(trace, "Initializing with a PiGS or InnoMakerIMX296GS_Mono camera." ); + GS_LOG_TRACE_MSG(trace, "Initializing with a PiGS or InnoMakerIMX296GS_Mono camera."); // Sensor pixel width is 3.45uM square? No - 6.33mm diagonal. It appears that // the actual width is the full resolution (1456) * 3.4uM = 4.95mm, // Not simply the diagonal sensor width sensor_width_ = (float)5.077365371; // 4.45; // (1456.0 * 3.4) / 1000; // = 4.95; // In mm 6.3 / sqrt(2.0); // TBD - Confirm math from diagonal measurement sensor_height_ = (float)3.789078635; // 4.45; //(1088.0 * 3.4) / 1000; // In mm 6.3 / sqrt(2.0); - + if (resolution_x_override_ > 0 && resolution_y_override_ > 0) { resolution_x_ = resolution_x_override_; resolution_y_ = resolution_y_override_; @@ -263,6 +264,43 @@ namespace golf_sim { video_resolution_y_ = 1080; GS_LOG_TRACE_MSG(trace, "Video resolution (x,y) is: " + std::to_string(video_resolution_x_) + "/" + std::to_string(video_resolution_y_) + "."); + } + + if (model == Mira220_Mono) { + + // See https://look.ams-osram.com/m/7591efdbe4af32dc/original/Mira220-1-2-7-2-2-MP-NIR-enhanced-global-shutter-image-sensor.pdf for details + + GS_LOG_TRACE_MSG(trace, "Initializing with a Mira220_Mono - based camera."); + // Sensor pixel width is 3.45uM square? No - 6.33mm diagonal. It appears that + // the actual width is the full resolution (1456) * 3.4uM = 4.95mm, + // Not simply the diagonal sensor width + + sensor_width_ = (float)4.464; // 2.79um pixel size * 1600 + sensor_height_ = (float)3.906; // 2.79um pixel size * 1400 + + if (resolution_x_override_ > 0 && resolution_y_override_ > 0) { + resolution_x_ = resolution_x_override_; + resolution_y_ = resolution_y_override_; + } + else { + // Defaults + resolution_x_ = 1600; + resolution_y_ = 1400; + } + + // We ahve not tested . + video_resolution_x_ = resolution_x_; + video_resolution_y_ = resolution_y_; + + GS_LOG_TRACE_MSG(trace, "Video resolution (x,y) is: " + std::to_string(video_resolution_x_) + "/" + std::to_string(video_resolution_y_) + "."); + } + + + // Ball radius parameters are common to most all of our potential cameras + if (model == PiGS || + model == InnoMakerIMX296GS_Mono || + model == Mira220_Mono) { + // Attempt to get the expected ball radius from the .json file std::string ball_radius_pixels_at_40cm_name = "kExpectedBallRadiusPixelsAt40cmCamera" + std::string(camera_number == GsCameraNumber::kGsCamera1 ? "1" : "2"); diff --git a/Software/LMSourceCode/ImageProcessing/camera_hardware.h b/Software/LMSourceCode/ImageProcessing/camera_hardware.h index 4b9fe9b7..e07aba42 100644 --- a/Software/LMSourceCode/ImageProcessing/camera_hardware.h +++ b/Software/LMSourceCode/ImageProcessing/camera_hardware.h @@ -36,6 +36,7 @@ namespace golf_sim { PiHQ = 3, PiGS = 4, InnoMakerIMX296GS_Mono = 5, + Mira220_Mono = 6, kCameraUnknown = 100 }; diff --git a/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp b/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp index da9d7c6f..aad834c4 100644 --- a/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp +++ b/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp @@ -753,8 +753,10 @@ bool ConfigureLibCameraOptions(const GolfSimCamera& camera, RPiCamEncoder& app, options->Set().timeout.set("100000s"); + // TBD - Internet suggests that the Mira220 also needs cdn_off const CameraHardware::CameraModel camera_model = GolfSimCamera::kSystemSlot1CameraType; - if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono) { + if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono && + camera_model != CameraHardware::CameraModel::Mira220_Mono) { options->Set().denoise = "cdn_off"; } else { @@ -1119,7 +1121,8 @@ LibcameraJpegApp* ConfigureForLibcameraStill(const GolfSimCamera& camera) { options->Set().contrast = camera_contrast; options->Set().timeout.set("100000s"); const CameraHardware::CameraModel camera_model = GolfSimCamera::kSystemSlot1CameraType; - if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono) { + if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono && + camera_model != CameraHardware::CameraModel::Mira220_Mono) { options->Set().denoise = "cdn_off"; } else { @@ -1419,7 +1422,8 @@ bool WaitForCam2Trigger(cv::Mat& return_image) { // JPMOD options->Set().timeout.set("100000s"); // Wait forever for external trigger const CameraHardware::CameraModel camera_model = GolfSimCamera::kSystemSlot1CameraType; - if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono) { + if (camera_model != CameraHardware::CameraModel::InnoMakerIMX296GS_Mono && + camera_model != CameraHardware::CameraModel::Mira220_Mono) { options->Set().denoise = "cdn_off"; } else { diff --git a/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp b/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp index 3c471c30..cd6fcb07 100644 --- a/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp +++ b/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp @@ -82,6 +82,22 @@ void SetExternalTrigger(bool& flag) { return; } } + + if (!flag && camera_model == gs::CameraHardware::CameraModel::Mira220_Mono) { + + flag = true; + + std::string trigger_mode_command = "$PITRAC_ROOT/ImageProcessing/CameraTools/NEED_EXTERNAL_TRIGGER_UTILITY 4 1"; + + GS_LOG_TRACE_MSG(trace, "ball_flight_camera_event_loop - Camera 2 trigger_mode_command = " + trigger_mode_command); + int command_result = system(trigger_mode_command.c_str()); + + if (command_result != 0) { + GS_LOG_TRACE_MSG(trace, "system(trigger_mode_command) failed."); + return; + } + } + } // Run the triggered capture event loop on an already-opened camera. @@ -101,7 +117,9 @@ bool cam2_run_event_loop(LibcameraJpegApp& app, cv::Mat& returnImg, bool send_pr // If appropriate, add the time we allow to setup external trigginer for the InnoMaker cameras const gs::CameraHardware::CameraModel camera_model = gs::GolfSimCamera::kSystemSlot2CameraType; - if (camera_model == gs::CameraHardware::CameraModel::InnoMakerIMX296GS_Mono) { + // TBD - May need a separate pause time for the Mira220 camera, but for now, we'll assume it's the same as the InnoMaker IMX296GS Mono camera + if (camera_model == gs::CameraHardware::CameraModel::InnoMakerIMX296GS_Mono || + camera_model == gs::CameraHardware::CameraModel::Mira220_Mono) { kQuiesceTimeMs += gs::PulseStrobe::kPauseToSetUpInnoMakerExternalTriggerMilliseconds; } diff --git a/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp b/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp index e340fde8..d3570375 100644 --- a/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp +++ b/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp @@ -649,6 +649,8 @@ namespace golf_sim { } } + // NOTE - We're not yet sure if a Mira220-based sensor will need any priming pulses. However, it should probably work similar to the IMX296 + GS_LOG_TRACE_MSG(trace, "Sent " + std::to_string(kNumberPrimingPulses) + " initial priming pulses. About to pause for " + std::to_string(kPauseBeforeReadyForFinalPrimingPulseMs) + " milliSeconds before sending penultimate priming pulse."); diff --git a/Software/web-server/configurations.json b/Software/web-server/configurations.json index a8bdeb23..3ff8a2fa 100644 --- a/Software/web-server/configurations.json +++ b/Software/web-server/configurations.json @@ -329,7 +329,8 @@ "2": "Pi Camera v2 - IMX219 sensor (DEPRECATED)", "3": "Pi HQ Camera - IMX477 sensor (DEPRECATED)", "4": "Pi Global Shutter - IMX296 Color", - "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)" + "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)", + "6": "Osram Mira220 - Mono sensor (EXPERIMENTAL)" }, "default": "5", "requiresRestart": true, @@ -391,7 +392,8 @@ "2": "Pi Camera v2 - IMX219 sensor (DEPRECATED)", "3": "Pi HQ Camera - IMX477 sensor (DEPRECATED)", "4": "Pi Global Shutter - IMX296 Color", - "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)" + "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)", + "6": "Osram Mira220 - Mono sensor (EXPERIMENTAL)" }, "default": "5", "requiresRestart": true, diff --git a/Software/web-server/server.py b/Software/web-server/server.py index 4210f062..6ce0c5a5 100644 --- a/Software/web-server/server.py +++ b/Software/web-server/server.py @@ -38,7 +38,7 @@ # Locked to defeat AE/AWB/tuning drift across calibration frames (sub-pixel # corner bias). Tuning file matches production rcPi5GS.sh; shutter is shorter # than production (11 vs 20 ms) to limit hand-tremor blur. -RPICAM_TUNING_FILE = "/usr/share/libcamera/ipa/rpi/pisp/imx296_noir.json" +RPICAM_TUNING_FILE = "/usr/share/libcamera/ipa/rpi/pisp/imx296_noir.json" # NOTE - Mira220 will need a different tuning file. For now, folks can just put the Mira220 file in this file RPICAM_CAL_SHUTTER_US = 11000 RPICAM_CAL_GAIN = 1.3