Chore: rename api->camera-management-api and web->management-dashboard-web-app; update compose, ignore, README references
This commit is contained in:
@@ -0,0 +1,607 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "intro",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Camera Status and Availability Testing\n",
|
||||
"\n",
|
||||
"This notebook tests various methods to check camera status and availability before attempting to capture images.\n",
|
||||
"\n",
|
||||
"## Key Functions to Test:\n",
|
||||
"- `CameraIsOpened()` - Check if camera is already opened by another process\n",
|
||||
"- `CameraInit()` - Try to initialize and catch specific error codes\n",
|
||||
"- `CameraGetImageBuffer()` - Test actual image capture with timeout\n",
|
||||
"- Error code analysis for different failure scenarios"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"id": "imports",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Libraries imported successfully!\n",
|
||||
"Platform: Linux\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Import required libraries\n",
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"import time\n",
|
||||
"import numpy as np\n",
|
||||
"import cv2\n",
|
||||
"import platform\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"# Add the python demo directory to path to import mvsdk\n",
|
||||
"sys.path.append('../python demo')\n",
|
||||
"import mvsdk\n",
|
||||
"\n",
|
||||
"print(\"Libraries imported successfully!\")\n",
|
||||
"print(f\"Platform: {platform.system()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"id": "error-codes",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Relevant Camera Status Error Codes:\n",
|
||||
"========================================\n",
|
||||
"CAMERA_STATUS_SUCCESS: 0\n",
|
||||
"CAMERA_STATUS_DEVICE_IS_OPENED: -18\n",
|
||||
"CAMERA_STATUS_DEVICE_IS_CLOSED: -19\n",
|
||||
"CAMERA_STATUS_ACCESS_DENY: -45\n",
|
||||
"CAMERA_STATUS_DEVICE_LOST: -38\n",
|
||||
"CAMERA_STATUS_TIME_OUT: -12\n",
|
||||
"CAMERA_STATUS_BUSY: -28\n",
|
||||
"CAMERA_STATUS_NO_DEVICE_FOUND: -16\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Let's examine the relevant error codes from the SDK\n",
|
||||
"print(\"Relevant Camera Status Error Codes:\")\n",
|
||||
"print(\"=\" * 40)\n",
|
||||
"print(f\"CAMERA_STATUS_SUCCESS: {mvsdk.CAMERA_STATUS_SUCCESS}\")\n",
|
||||
"print(f\"CAMERA_STATUS_DEVICE_IS_OPENED: {mvsdk.CAMERA_STATUS_DEVICE_IS_OPENED}\")\n",
|
||||
"print(f\"CAMERA_STATUS_DEVICE_IS_CLOSED: {mvsdk.CAMERA_STATUS_DEVICE_IS_CLOSED}\")\n",
|
||||
"print(f\"CAMERA_STATUS_ACCESS_DENY: {mvsdk.CAMERA_STATUS_ACCESS_DENY}\")\n",
|
||||
"print(f\"CAMERA_STATUS_DEVICE_LOST: {mvsdk.CAMERA_STATUS_DEVICE_LOST}\")\n",
|
||||
"print(f\"CAMERA_STATUS_TIME_OUT: {mvsdk.CAMERA_STATUS_TIME_OUT}\")\n",
|
||||
"print(f\"CAMERA_STATUS_BUSY: {mvsdk.CAMERA_STATUS_BUSY}\")\n",
|
||||
"print(f\"CAMERA_STATUS_NO_DEVICE_FOUND: {mvsdk.CAMERA_STATUS_NO_DEVICE_FOUND}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"id": "status-functions",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Camera Availability Check\n",
|
||||
"==============================\n",
|
||||
"✓ SDK initialized successfully\n",
|
||||
"✓ Found 2 camera(s)\n",
|
||||
" 0: Blower-Yield-Cam (192.168.1.165-192.168.1.54)\n",
|
||||
" 1: Cracker-Cam (192.168.1.167-192.168.1.54)\n",
|
||||
"\n",
|
||||
"Testing camera 0: Blower-Yield-Cam\n",
|
||||
"✓ Camera is available (not opened by another process)\n",
|
||||
"✓ Camera initialized successfully\n",
|
||||
"✓ Camera closed after testing\n",
|
||||
"\n",
|
||||
"Testing camera 1: Cracker-Cam\n",
|
||||
"✓ Camera is available (not opened by another process)\n",
|
||||
"✓ Camera initialized successfully\n",
|
||||
"✓ Camera closed after testing\n",
|
||||
"\n",
|
||||
"Results for 2 cameras:\n",
|
||||
" Camera 0: AVAILABLE\n",
|
||||
" Camera 1: AVAILABLE\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def check_camera_availability():\n",
|
||||
" \"\"\"\n",
|
||||
" Comprehensive camera availability check\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"Camera Availability Check\")\n",
|
||||
" print(\"=\" * 30)\n",
|
||||
" \n",
|
||||
" # Step 1: Initialize SDK\n",
|
||||
" try:\n",
|
||||
" mvsdk.CameraSdkInit(1)\n",
|
||||
" print(\"✓ SDK initialized successfully\")\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"✗ SDK initialization failed: {e}\")\n",
|
||||
" return None, \"SDK_INIT_FAILED\"\n",
|
||||
" \n",
|
||||
" # Step 2: Enumerate cameras\n",
|
||||
" try:\n",
|
||||
" DevList = mvsdk.CameraEnumerateDevice()\n",
|
||||
" nDev = len(DevList)\n",
|
||||
" print(f\"✓ Found {nDev} camera(s)\")\n",
|
||||
" \n",
|
||||
" if nDev < 1:\n",
|
||||
" print(\"✗ No cameras detected\")\n",
|
||||
" return None, \"NO_CAMERAS\"\n",
|
||||
" \n",
|
||||
" for i, DevInfo in enumerate(DevList):\n",
|
||||
" print(f\" {i}: {DevInfo.GetFriendlyName()} ({DevInfo.GetPortType()})\")\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"✗ Camera enumeration failed: {e}\")\n",
|
||||
" return None, \"ENUM_FAILED\"\n",
|
||||
" \n",
|
||||
" # Step 3: Check all cameras\n",
|
||||
" camera_results = []\n",
|
||||
" \n",
|
||||
" for i, DevInfo in enumerate(DevList):\n",
|
||||
" print(f\"\\nTesting camera {i}: {DevInfo.GetFriendlyName()}\")\n",
|
||||
" \n",
|
||||
" # Check if camera is already opened\n",
|
||||
" try:\n",
|
||||
" is_opened = mvsdk.CameraIsOpened(DevInfo)\n",
|
||||
" if is_opened:\n",
|
||||
" print(\"✗ Camera is already opened by another process\")\n",
|
||||
" camera_results.append((DevInfo, \"ALREADY_OPENED\"))\n",
|
||||
" continue\n",
|
||||
" else:\n",
|
||||
" print(\"✓ Camera is available (not opened by another process)\")\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"⚠ Could not check if camera is opened: {e}\")\n",
|
||||
" \n",
|
||||
" # Try to initialize camera\n",
|
||||
" try:\n",
|
||||
" hCamera = mvsdk.CameraInit(DevInfo, -1, -1)\n",
|
||||
" print(\"✓ Camera initialized successfully\")\n",
|
||||
" camera_results.append((hCamera, \"AVAILABLE\"))\n",
|
||||
" \n",
|
||||
" # Close the camera after testing\n",
|
||||
" try:\n",
|
||||
" mvsdk.CameraUnInit(hCamera)\n",
|
||||
" print(\"✓ Camera closed after testing\")\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"⚠ Warning: Could not close camera: {e}\")\n",
|
||||
" \n",
|
||||
" except mvsdk.CameraException as e:\n",
|
||||
" print(f\"✗ Camera initialization failed: {e.error_code} - {e.message}\")\n",
|
||||
" \n",
|
||||
" # Analyze specific error codes\n",
|
||||
" if e.error_code == mvsdk.CAMERA_STATUS_DEVICE_IS_OPENED:\n",
|
||||
" camera_results.append((DevInfo, \"DEVICE_OPENED\"))\n",
|
||||
" elif e.error_code == mvsdk.CAMERA_STATUS_ACCESS_DENY:\n",
|
||||
" camera_results.append((DevInfo, \"ACCESS_DENIED\"))\n",
|
||||
" elif e.error_code == mvsdk.CAMERA_STATUS_DEVICE_LOST:\n",
|
||||
" camera_results.append((DevInfo, \"DEVICE_LOST\"))\n",
|
||||
" else:\n",
|
||||
" camera_results.append((DevInfo, f\"INIT_ERROR_{e.error_code}\"))\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"✗ Unexpected error during initialization: {e}\")\n",
|
||||
" camera_results.append((DevInfo, \"UNEXPECTED_ERROR\"))\n",
|
||||
" \n",
|
||||
" return camera_results\n",
|
||||
"\n",
|
||||
"# Test the function\n",
|
||||
"camera_results = check_camera_availability()\n",
|
||||
"print(f\"\\nResults for {len(camera_results)} cameras:\")\n",
|
||||
"for i, (camera_info, status) in enumerate(camera_results):\n",
|
||||
" if hasattr(camera_info, 'GetFriendlyName'):\n",
|
||||
" name = camera_info.GetFriendlyName()\n",
|
||||
" else:\n",
|
||||
" name = f\"Camera {i}\"\n",
|
||||
" print(f\" {name}: {status}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"id": "test-capture-availability",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Testing capture readiness for 2 available camera(s):\n",
|
||||
"\n",
|
||||
"Testing camera 0 capture readiness...\n",
|
||||
"\n",
|
||||
"Testing Camera Capture Readiness\n",
|
||||
"===================================\n",
|
||||
"✓ Camera capabilities retrieved\n",
|
||||
"✓ Camera type: Color\n",
|
||||
"✓ Basic camera configuration set\n",
|
||||
"✓ Camera started\n",
|
||||
"✓ Frame buffer allocated\n",
|
||||
"\n",
|
||||
"Testing image capture...\n",
|
||||
"✓ Image captured successfully: 1280x1024\n",
|
||||
"✓ Image processed and buffer released\n",
|
||||
"✓ Cleanup completed\n",
|
||||
"Capture Ready for Blower-Yield-Cam: True\n",
|
||||
"\n",
|
||||
"Testing camera 1 capture readiness...\n",
|
||||
"\n",
|
||||
"Testing Camera Capture Readiness\n",
|
||||
"===================================\n",
|
||||
"✓ Camera capabilities retrieved\n",
|
||||
"✓ Camera type: Color\n",
|
||||
"✓ Basic camera configuration set\n",
|
||||
"✓ Camera started\n",
|
||||
"✓ Frame buffer allocated\n",
|
||||
"\n",
|
||||
"Testing image capture...\n",
|
||||
"✓ Image captured successfully: 1280x1024\n",
|
||||
"✓ Image processed and buffer released\n",
|
||||
"✓ Cleanup completed\n",
|
||||
"Capture Ready for Cracker-Cam: True\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def test_camera_capture_readiness(hCamera):\n",
|
||||
" \"\"\"\n",
|
||||
" Test if camera is ready for image capture\n",
|
||||
" \"\"\"\n",
|
||||
" if not isinstance(hCamera, int):\n",
|
||||
" print(\"Camera not properly initialized, skipping capture test\")\n",
|
||||
" return False\n",
|
||||
" \n",
|
||||
" print(\"\\nTesting Camera Capture Readiness\")\n",
|
||||
" print(\"=\" * 35)\n",
|
||||
" \n",
|
||||
" try:\n",
|
||||
" # Get camera capabilities\n",
|
||||
" cap = mvsdk.CameraGetCapability(hCamera)\n",
|
||||
" print(\"✓ Camera capabilities retrieved\")\n",
|
||||
" \n",
|
||||
" # Check camera type\n",
|
||||
" monoCamera = (cap.sIspCapacity.bMonoSensor != 0)\n",
|
||||
" print(f\"✓ Camera type: {'Monochrome' if monoCamera else 'Color'}\")\n",
|
||||
" \n",
|
||||
" # Set basic configuration\n",
|
||||
" if monoCamera:\n",
|
||||
" mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_MONO8)\n",
|
||||
" else:\n",
|
||||
" mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_BGR8)\n",
|
||||
" \n",
|
||||
" mvsdk.CameraSetTriggerMode(hCamera, 0) # Continuous mode\n",
|
||||
" mvsdk.CameraSetAeState(hCamera, 0) # Manual exposure\n",
|
||||
" mvsdk.CameraSetExposureTime(hCamera, 5000) # 5ms exposure\n",
|
||||
" print(\"✓ Basic camera configuration set\")\n",
|
||||
" \n",
|
||||
" # Start camera\n",
|
||||
" mvsdk.CameraPlay(hCamera)\n",
|
||||
" print(\"✓ Camera started\")\n",
|
||||
" \n",
|
||||
" # Allocate buffer\n",
|
||||
" FrameBufferSize = cap.sResolutionRange.iWidthMax * cap.sResolutionRange.iHeightMax * (1 if monoCamera else 3)\n",
|
||||
" pFrameBuffer = mvsdk.CameraAlignMalloc(FrameBufferSize, 16)\n",
|
||||
" print(\"✓ Frame buffer allocated\")\n",
|
||||
" \n",
|
||||
" # Test image capture with short timeout\n",
|
||||
" print(\"\\nTesting image capture...\")\n",
|
||||
" try:\n",
|
||||
" pRawData, FrameHead = mvsdk.CameraGetImageBuffer(hCamera, 1000) # 1 second timeout\n",
|
||||
" print(f\"✓ Image captured successfully: {FrameHead.iWidth}x{FrameHead.iHeight}\")\n",
|
||||
" \n",
|
||||
" # Process and release\n",
|
||||
" mvsdk.CameraImageProcess(hCamera, pRawData, pFrameBuffer, FrameHead)\n",
|
||||
" mvsdk.CameraReleaseImageBuffer(hCamera, pRawData)\n",
|
||||
" print(\"✓ Image processed and buffer released\")\n",
|
||||
" \n",
|
||||
" capture_success = True\n",
|
||||
" \n",
|
||||
" except mvsdk.CameraException as e:\n",
|
||||
" print(f\"✗ Image capture failed: {e.error_code} - {e.message}\")\n",
|
||||
" \n",
|
||||
" if e.error_code == mvsdk.CAMERA_STATUS_TIME_OUT:\n",
|
||||
" print(\" → Camera timeout - may be busy or not streaming\")\n",
|
||||
" elif e.error_code == mvsdk.CAMERA_STATUS_DEVICE_LOST:\n",
|
||||
" print(\" → Device lost - camera disconnected\")\n",
|
||||
" elif e.error_code == mvsdk.CAMERA_STATUS_BUSY:\n",
|
||||
" print(\" → Camera busy - may be used by another process\")\n",
|
||||
" \n",
|
||||
" capture_success = False\n",
|
||||
" \n",
|
||||
" # Cleanup\n",
|
||||
" mvsdk.CameraAlignFree(pFrameBuffer)\n",
|
||||
" print(\"✓ Cleanup completed\")\n",
|
||||
" \n",
|
||||
" return capture_success\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"✗ Capture readiness test failed: {e}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
"# Test capture readiness for available cameras\n",
|
||||
"available_cameras = [(cam, stat) for cam, stat in camera_results if stat == \"AVAILABLE\"]\n",
|
||||
"\n",
|
||||
"if available_cameras:\n",
|
||||
" print(f\"\\nTesting capture readiness for {len(available_cameras)} available camera(s):\")\n",
|
||||
" for i, (camera_handle, status) in enumerate(available_cameras):\n",
|
||||
" if hasattr(camera_handle, 'GetFriendlyName'):\n",
|
||||
" # This shouldn't happen for AVAILABLE cameras, but just in case\n",
|
||||
" print(f\"\\nCamera {i}: Invalid handle\")\n",
|
||||
" continue\n",
|
||||
" \n",
|
||||
" print(f\"\\nTesting camera {i} capture readiness...\")\n",
|
||||
" # Re-initialize the camera for testing since we closed it earlier\n",
|
||||
" try:\n",
|
||||
" # Find the camera info from the original results\n",
|
||||
" DevList = mvsdk.CameraEnumerateDevice()\n",
|
||||
" if i < len(DevList):\n",
|
||||
" DevInfo = DevList[i]\n",
|
||||
" hCamera = mvsdk.CameraInit(DevInfo, -1, -1)\n",
|
||||
" capture_ready = test_camera_capture_readiness(hCamera)\n",
|
||||
" print(f\"Capture Ready for {DevInfo.GetFriendlyName()}: {capture_ready}\")\n",
|
||||
" mvsdk.CameraUnInit(hCamera)\n",
|
||||
" else:\n",
|
||||
" print(f\"Could not re-initialize camera {i}\")\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Error testing camera {i}: {e}\")\n",
|
||||
"else:\n",
|
||||
" print(\"\\nNo cameras are available for capture testing\")\n",
|
||||
" print(\"Camera statuses:\")\n",
|
||||
" for i, (cam_info, status) in enumerate(camera_results):\n",
|
||||
" if hasattr(cam_info, 'GetFriendlyName'):\n",
|
||||
" name = cam_info.GetFriendlyName()\n",
|
||||
" else:\n",
|
||||
" name = f\"Camera {i}\"\n",
|
||||
" print(f\" {name}: {status}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"id": "comprehensive-check",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"==================================================\n",
|
||||
"COMPREHENSIVE CAMERA CHECK\n",
|
||||
"==================================================\n",
|
||||
"Camera Availability Check\n",
|
||||
"==============================\n",
|
||||
"✓ SDK initialized successfully\n",
|
||||
"✓ Found 2 camera(s)\n",
|
||||
" 0: Blower-Yield-Cam (192.168.1.165-192.168.1.54)\n",
|
||||
" 1: Cracker-Cam (192.168.1.167-192.168.1.54)\n",
|
||||
"\n",
|
||||
"Testing camera 0: Blower-Yield-Cam\n",
|
||||
"✓ Camera is available (not opened by another process)\n",
|
||||
"✓ Camera initialized successfully\n",
|
||||
"✓ Camera closed after testing\n",
|
||||
"\n",
|
||||
"Testing camera 1: Cracker-Cam\n",
|
||||
"✓ Camera is available (not opened by another process)\n",
|
||||
"✓ Camera initialized successfully\n",
|
||||
"✓ Camera closed after testing\n",
|
||||
"\n",
|
||||
"==================================================\n",
|
||||
"FINAL RESULTS:\n",
|
||||
"Camera Available: False\n",
|
||||
"Capture Ready: False\n",
|
||||
"Status: (42, 'AVAILABLE')\n",
|
||||
"==================================================\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def comprehensive_camera_check():\n",
|
||||
" \"\"\"\n",
|
||||
" Complete camera availability and readiness check\n",
|
||||
" Returns: (available, ready, handle_or_info, status_message)\n",
|
||||
" \"\"\"\n",
|
||||
" # Check availability\n",
|
||||
" handle_or_info, status = check_camera_availability()\n",
|
||||
" \n",
|
||||
" available = status == \"AVAILABLE\"\n",
|
||||
" ready = False\n",
|
||||
" \n",
|
||||
" if available:\n",
|
||||
" # Test capture readiness\n",
|
||||
" ready = test_camera_capture_readiness(handle_or_info)\n",
|
||||
" \n",
|
||||
" # Close camera after testing\n",
|
||||
" try:\n",
|
||||
" mvsdk.CameraUnInit(handle_or_info)\n",
|
||||
" print(\"✓ Camera closed after testing\")\n",
|
||||
" except:\n",
|
||||
" pass\n",
|
||||
" \n",
|
||||
" return available, ready, handle_or_info, status\n",
|
||||
"\n",
|
||||
"# Run comprehensive check\n",
|
||||
"print(\"\\n\" + \"=\" * 50)\n",
|
||||
"print(\"COMPREHENSIVE CAMERA CHECK\")\n",
|
||||
"print(\"=\" * 50)\n",
|
||||
"\n",
|
||||
"available, ready, info, status_msg = comprehensive_camera_check()\n",
|
||||
"\n",
|
||||
"print(\"\\n\" + \"=\" * 50)\n",
|
||||
"print(\"FINAL RESULTS:\")\n",
|
||||
"print(f\"Camera Available: {available}\")\n",
|
||||
"print(f\"Capture Ready: {ready}\")\n",
|
||||
"print(f\"Status: {status_msg}\")\n",
|
||||
"print(\"=\" * 50)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"id": "status-check-function",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Testing Simple Camera Ready Check:\n",
|
||||
"========================================\n",
|
||||
"Ready: True\n",
|
||||
"Message: Camera 'Blower-Yield-Cam' is ready for capture\n",
|
||||
"Camera: Blower-Yield-Cam\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def is_camera_ready_for_capture():\n",
|
||||
" \"\"\"\n",
|
||||
" Simple function to check if camera is ready for capture.\n",
|
||||
" Returns: (ready: bool, message: str, camera_info: object or None)\n",
|
||||
" \n",
|
||||
" This is the function you can use in your main capture script.\n",
|
||||
" \"\"\"\n",
|
||||
" try:\n",
|
||||
" # Initialize SDK\n",
|
||||
" mvsdk.CameraSdkInit(1)\n",
|
||||
" \n",
|
||||
" # Enumerate cameras\n",
|
||||
" DevList = mvsdk.CameraEnumerateDevice()\n",
|
||||
" if len(DevList) < 1:\n",
|
||||
" return False, \"No cameras found\", None\n",
|
||||
" \n",
|
||||
" DevInfo = DevList[0]\n",
|
||||
" \n",
|
||||
" # Check if already opened\n",
|
||||
" try:\n",
|
||||
" if mvsdk.CameraIsOpened(DevInfo):\n",
|
||||
" return False, f\"Camera '{DevInfo.GetFriendlyName()}' is already opened by another process\", DevInfo\n",
|
||||
" except:\n",
|
||||
" pass # Some cameras might not support this check\n",
|
||||
" \n",
|
||||
" # Try to initialize\n",
|
||||
" try:\n",
|
||||
" hCamera = mvsdk.CameraInit(DevInfo, -1, -1)\n",
|
||||
" \n",
|
||||
" # Quick capture test\n",
|
||||
" try:\n",
|
||||
" # Basic setup\n",
|
||||
" mvsdk.CameraSetTriggerMode(hCamera, 0)\n",
|
||||
" mvsdk.CameraPlay(hCamera)\n",
|
||||
" \n",
|
||||
" # Try to get one frame with short timeout\n",
|
||||
" pRawData, FrameHead = mvsdk.CameraGetImageBuffer(hCamera, 500) # 0.5 second timeout\n",
|
||||
" mvsdk.CameraReleaseImageBuffer(hCamera, pRawData)\n",
|
||||
" \n",
|
||||
" # Success - close and return\n",
|
||||
" mvsdk.CameraUnInit(hCamera)\n",
|
||||
" return True, f\"Camera '{DevInfo.GetFriendlyName()}' is ready for capture\", DevInfo\n",
|
||||
" \n",
|
||||
" except mvsdk.CameraException as e:\n",
|
||||
" mvsdk.CameraUnInit(hCamera)\n",
|
||||
" if e.error_code == mvsdk.CAMERA_STATUS_TIME_OUT:\n",
|
||||
" return False, \"Camera timeout - may be busy or not streaming properly\", DevInfo\n",
|
||||
" else:\n",
|
||||
" return False, f\"Camera capture test failed: {e.message}\", DevInfo\n",
|
||||
" \n",
|
||||
" except mvsdk.CameraException as e:\n",
|
||||
" if e.error_code == mvsdk.CAMERA_STATUS_DEVICE_IS_OPENED:\n",
|
||||
" return False, f\"Camera '{DevInfo.GetFriendlyName()}' is already in use\", DevInfo\n",
|
||||
" elif e.error_code == mvsdk.CAMERA_STATUS_ACCESS_DENY:\n",
|
||||
" return False, f\"Access denied to camera '{DevInfo.GetFriendlyName()}'\", DevInfo\n",
|
||||
" else:\n",
|
||||
" return False, f\"Camera initialization failed: {e.message}\", DevInfo\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" return False, f\"Camera check failed: {str(e)}\", None\n",
|
||||
"\n",
|
||||
"# Test the simple function\n",
|
||||
"print(\"\\nTesting Simple Camera Ready Check:\")\n",
|
||||
"print(\"=\" * 40)\n",
|
||||
"\n",
|
||||
"ready, message, camera_info = is_camera_ready_for_capture()\n",
|
||||
"print(f\"Ready: {ready}\")\n",
|
||||
"print(f\"Message: {message}\")\n",
|
||||
"if camera_info:\n",
|
||||
" print(f\"Camera: {camera_info.GetFriendlyName()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "usage-example",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage Example\n",
|
||||
"\n",
|
||||
"Here's how you can integrate the camera status check into your capture script:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"# Before attempting to capture images\n",
|
||||
"ready, message, camera_info = is_camera_ready_for_capture()\n",
|
||||
"\n",
|
||||
"if not ready:\n",
|
||||
" print(f\"Camera not ready: {message}\")\n",
|
||||
" # Handle the error appropriately\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
"print(f\"Camera ready: {message}\")\n",
|
||||
"# Proceed with normal capture logic\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"## Key Findings\n",
|
||||
"\n",
|
||||
"1. **`CameraIsOpened()`** - Checks if camera is opened by another process\n",
|
||||
"2. **`CameraInit()` error codes** - Provide specific failure reasons\n",
|
||||
"3. **Quick capture test** - Verifies camera is actually streaming\n",
|
||||
"4. **Timeout handling** - Detects if camera is busy/unresponsive\n",
|
||||
"\n",
|
||||
"The most reliable approach is to:\n",
|
||||
"1. Check if camera exists\n",
|
||||
"2. Check if it's already opened\n",
|
||||
"3. Try to initialize it\n",
|
||||
"4. Test actual image capture with short timeout\n",
|
||||
"5. Clean up properly"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "USDA-vision-cameras",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# GigE Camera Test Setup\n",
|
||||
"\n",
|
||||
"This notebook helps you test and configure your GigE cameras for the USDA vision project.\n",
|
||||
"\n",
|
||||
"## Key Features:\n",
|
||||
"- Test camera connectivity\n",
|
||||
"- Display images inline (no GUI needed)\n",
|
||||
"- Save test images/videos to `/storage`\n",
|
||||
"- Configure camera parameters\n",
|
||||
"- Test recording functionality"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"✅ All imports successful!\n",
|
||||
"OpenCV version: 4.11.0\n",
|
||||
"NumPy version: 2.3.2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import cv2\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"import time\n",
|
||||
"from pathlib import Path\n",
|
||||
"import imageio\n",
|
||||
"from tqdm import tqdm\n",
|
||||
"\n",
|
||||
"# Configure matplotlib for inline display\n",
|
||||
"plt.rcParams['figure.figsize'] = (12, 8)\n",
|
||||
"plt.rcParams['image.cmap'] = 'gray'\n",
|
||||
"\n",
|
||||
"print(\"✅ All imports successful!\")\n",
|
||||
"print(f\"OpenCV version: {cv2.__version__}\")\n",
|
||||
"print(f\"NumPy version: {np.__version__}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Utility Functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"✅ Utility functions loaded!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def display_image(image, title=\"Image\", figsize=(10, 8)):\n",
|
||||
" \"\"\"Display image inline in Jupyter notebook\"\"\"\n",
|
||||
" plt.figure(figsize=figsize)\n",
|
||||
" if len(image.shape) == 3:\n",
|
||||
" # Convert BGR to RGB for matplotlib\n",
|
||||
" image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
|
||||
" plt.imshow(image_rgb)\n",
|
||||
" else:\n",
|
||||
" plt.imshow(image, cmap='gray')\n",
|
||||
" plt.title(title)\n",
|
||||
" plt.axis('off')\n",
|
||||
" plt.tight_layout()\n",
|
||||
" plt.show()\n",
|
||||
"\n",
|
||||
"def save_image_to_storage(image, filename_prefix=\"test_image\"):\n",
|
||||
" \"\"\"Save image to /storage with timestamp\"\"\"\n",
|
||||
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" filename = f\"{filename_prefix}_{timestamp}.jpg\"\n",
|
||||
" filepath = f\"/storage/{filename}\"\n",
|
||||
" \n",
|
||||
" success = cv2.imwrite(filepath, image)\n",
|
||||
" if success:\n",
|
||||
" print(f\"✅ Image saved: {filepath}\")\n",
|
||||
" return filepath\n",
|
||||
" else:\n",
|
||||
" print(f\"❌ Failed to save image: {filepath}\")\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
"def create_storage_subdir(subdir_name):\n",
|
||||
" \"\"\"Create subdirectory in /storage\"\"\"\n",
|
||||
" path = Path(f\"/storage/{subdir_name}\")\n",
|
||||
" path.mkdir(exist_ok=True)\n",
|
||||
" print(f\"📁 Directory ready: {path}\")\n",
|
||||
" return str(path)\n",
|
||||
"\n",
|
||||
"def list_available_cameras():\n",
|
||||
" \"\"\"List all available camera devices\"\"\"\n",
|
||||
" print(\"🔍 Scanning for available cameras...\")\n",
|
||||
" available_cameras = []\n",
|
||||
" \n",
|
||||
" # Test camera indices 0-10\n",
|
||||
" for i in range(11):\n",
|
||||
" cap = cv2.VideoCapture(i)\n",
|
||||
" if cap.isOpened():\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" available_cameras.append(i)\n",
|
||||
" print(f\"📷 Camera {i}: Available (Resolution: {frame.shape[1]}x{frame.shape[0]})\")\n",
|
||||
" cap.release()\n",
|
||||
" else:\n",
|
||||
" # Try with different backends for GigE cameras\n",
|
||||
" cap = cv2.VideoCapture(i, cv2.CAP_GSTREAMER)\n",
|
||||
" if cap.isOpened():\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" available_cameras.append(i)\n",
|
||||
" print(f\"📷 Camera {i}: Available via GStreamer (Resolution: {frame.shape[1]}x{frame.shape[0]})\")\n",
|
||||
" cap.release()\n",
|
||||
" \n",
|
||||
" if not available_cameras:\n",
|
||||
" print(\"❌ No cameras found\")\n",
|
||||
" \n",
|
||||
" return available_cameras\n",
|
||||
"\n",
|
||||
"print(\"✅ Utility functions loaded!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Step 1: Check Storage Directory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Storage directory exists: True\n",
|
||||
"Storage directory writable: True\n",
|
||||
"📁 Directory ready: /storage/test_images\n",
|
||||
"📁 Directory ready: /storage/test_videos\n",
|
||||
"📁 Directory ready: /storage/camera1\n",
|
||||
"📁 Directory ready: /storage/camera2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Check storage directory\n",
|
||||
"storage_path = Path(\"/storage\")\n",
|
||||
"print(f\"Storage directory exists: {storage_path.exists()}\")\n",
|
||||
"print(f\"Storage directory writable: {os.access('/storage', os.W_OK)}\")\n",
|
||||
"\n",
|
||||
"# Create test subdirectories\n",
|
||||
"test_images_dir = create_storage_subdir(\"test_images\")\n",
|
||||
"test_videos_dir = create_storage_subdir(\"test_videos\")\n",
|
||||
"camera1_dir = create_storage_subdir(\"camera1\")\n",
|
||||
"camera2_dir = create_storage_subdir(\"camera2\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Step 2: Scan for Available Cameras"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"🔍 Scanning for available cameras...\n",
|
||||
"❌ No cameras found\n",
|
||||
"\n",
|
||||
"📊 Summary: Found 0 camera(s): []\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[ WARN:0@9.977] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video0): can't open camera by index\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.977] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.977] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video1): can't open camera by index\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.977] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.977] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video2): can't open camera by index\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.977] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.977] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video3): can't open camera by index\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.977] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.977] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.977] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video4): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.978] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.978] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video5): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.978] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.978] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video6): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.978] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.978] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video7): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.978] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.978] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video8): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.978] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.978] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video9): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.978] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@9.978] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video10): can't open camera by index\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.978] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@9.979] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@9.979] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@9.979] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Scan for cameras\n",
|
||||
"cameras = list_available_cameras()\n",
|
||||
"print(f\"\\n📊 Summary: Found {len(cameras)} camera(s): {cameras}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Step 3: Test Individual Camera"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"🔧 Testing camera 0...\n",
|
||||
" Trying Default backend...\n",
|
||||
" ❌ Default backend failed to open\n",
|
||||
" Trying GStreamer backend...\n",
|
||||
" ❌ GStreamer backend failed to open\n",
|
||||
" Trying V4L2 backend...\n",
|
||||
" ❌ V4L2 backend failed to open\n",
|
||||
" Trying FFmpeg backend...\n",
|
||||
" ❌ FFmpeg backend failed to open\n",
|
||||
"❌ Camera 0 not accessible with any backend\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[ WARN:0@27.995] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video0): can't open camera by index\n",
|
||||
"[ WARN:0@27.995] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@27.995] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ WARN:0@27.995] global obsensor_stream_channel_v4l2.cpp:82 xioctl ioctl: fd=-1, req=-2140645888\n",
|
||||
"[ WARN:0@27.995] global obsensor_stream_channel_v4l2.cpp:138 queryUvcDeviceInfoList ioctl error return: 9\n",
|
||||
"[ERROR:0@27.995] global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range\n",
|
||||
"[ WARN:0@27.996] global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video0): can't open camera by index\n",
|
||||
"[ WARN:0@27.996] global cap.cpp:478 open VIDEOIO(V4L2): backend is generally available but can't be used to capture by index\n",
|
||||
"[ WARN:0@27.996] global cap.cpp:478 open VIDEOIO(FFMPEG): backend is generally available but can't be used to capture by index\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Test a specific camera (change camera_id as needed)\n",
|
||||
"camera_id = 0 # Change this to test different cameras\n",
|
||||
"\n",
|
||||
"print(f\"🔧 Testing camera {camera_id}...\")\n",
|
||||
"\n",
|
||||
"# Try different backends for GigE cameras\n",
|
||||
"backends_to_try = [\n",
|
||||
" (cv2.CAP_ANY, \"Default\"),\n",
|
||||
" (cv2.CAP_GSTREAMER, \"GStreamer\"),\n",
|
||||
" (cv2.CAP_V4L2, \"V4L2\"),\n",
|
||||
" (cv2.CAP_FFMPEG, \"FFmpeg\")\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"successful_backend = None\n",
|
||||
"cap = None\n",
|
||||
"\n",
|
||||
"for backend, name in backends_to_try:\n",
|
||||
" print(f\" Trying {name} backend...\")\n",
|
||||
" cap = cv2.VideoCapture(camera_id, backend)\n",
|
||||
" if cap.isOpened():\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" print(f\" ✅ {name} backend works!\")\n",
|
||||
" successful_backend = (backend, name)\n",
|
||||
" break\n",
|
||||
" else:\n",
|
||||
" print(f\" ❌ {name} backend opened but can't read frames\")\n",
|
||||
" else:\n",
|
||||
" print(f\" ❌ {name} backend failed to open\")\n",
|
||||
" cap.release()\n",
|
||||
"\n",
|
||||
"if successful_backend:\n",
|
||||
" backend, backend_name = successful_backend\n",
|
||||
" cap = cv2.VideoCapture(camera_id, backend)\n",
|
||||
" \n",
|
||||
" # Get camera properties\n",
|
||||
" width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n",
|
||||
" height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n",
|
||||
" fps = cap.get(cv2.CAP_PROP_FPS)\n",
|
||||
" \n",
|
||||
" print(f\"\\n📷 Camera {camera_id} Properties ({backend_name}):\")\n",
|
||||
" print(f\" Resolution: {width}x{height}\")\n",
|
||||
" print(f\" FPS: {fps}\")\n",
|
||||
" \n",
|
||||
" # Capture a test frame\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" print(f\" Frame shape: {frame.shape}\")\n",
|
||||
" print(f\" Frame dtype: {frame.dtype}\")\n",
|
||||
" \n",
|
||||
" # Display the frame\n",
|
||||
" display_image(frame, f\"Camera {camera_id} Test Frame\")\n",
|
||||
" \n",
|
||||
" # Save test image\n",
|
||||
" save_image_to_storage(frame, f\"camera_{camera_id}_test\")\n",
|
||||
" else:\n",
|
||||
" print(\" ❌ Failed to capture frame\")\n",
|
||||
" \n",
|
||||
" cap.release()\n",
|
||||
"else:\n",
|
||||
" print(f\"❌ Camera {camera_id} not accessible with any backend\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Step 4: Test Video Recording"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test video recording\n",
|
||||
"def test_video_recording(camera_id, duration_seconds=5, fps=30):\n",
|
||||
" \"\"\"Test video recording from camera\"\"\"\n",
|
||||
" print(f\"🎥 Testing video recording from camera {camera_id} for {duration_seconds} seconds...\")\n",
|
||||
" \n",
|
||||
" # Open camera\n",
|
||||
" cap = cv2.VideoCapture(camera_id)\n",
|
||||
" if not cap.isOpened():\n",
|
||||
" print(f\"❌ Cannot open camera {camera_id}\")\n",
|
||||
" return None\n",
|
||||
" \n",
|
||||
" # Get camera properties\n",
|
||||
" width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n",
|
||||
" height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n",
|
||||
" \n",
|
||||
" # Create video writer\n",
|
||||
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" video_filename = f\"/storage/test_videos/camera_{camera_id}_test_{timestamp}.mp4\"\n",
|
||||
" \n",
|
||||
" fourcc = cv2.VideoWriter_fourcc(*'mp4v')\n",
|
||||
" out = cv2.VideoWriter(video_filename, fourcc, fps, (width, height))\n",
|
||||
" \n",
|
||||
" if not out.isOpened():\n",
|
||||
" print(\"❌ Cannot create video writer\")\n",
|
||||
" cap.release()\n",
|
||||
" return None\n",
|
||||
" \n",
|
||||
" # Record video\n",
|
||||
" frames_to_capture = duration_seconds * fps\n",
|
||||
" frames_captured = 0\n",
|
||||
" \n",
|
||||
" print(f\"Recording {frames_to_capture} frames...\")\n",
|
||||
" \n",
|
||||
" with tqdm(total=frames_to_capture, desc=\"Recording\") as pbar:\n",
|
||||
" start_time = time.time()\n",
|
||||
" \n",
|
||||
" while frames_captured < frames_to_capture:\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" out.write(frame)\n",
|
||||
" frames_captured += 1\n",
|
||||
" pbar.update(1)\n",
|
||||
" \n",
|
||||
" # Display first frame\n",
|
||||
" if frames_captured == 1:\n",
|
||||
" display_image(frame, f\"First frame from camera {camera_id}\")\n",
|
||||
" else:\n",
|
||||
" print(f\"❌ Failed to read frame {frames_captured}\")\n",
|
||||
" break\n",
|
||||
" \n",
|
||||
" # Cleanup\n",
|
||||
" cap.release()\n",
|
||||
" out.release()\n",
|
||||
" \n",
|
||||
" elapsed_time = time.time() - start_time\n",
|
||||
" actual_fps = frames_captured / elapsed_time\n",
|
||||
" \n",
|
||||
" print(f\"✅ Video saved: {video_filename}\")\n",
|
||||
" print(f\"📊 Captured {frames_captured} frames in {elapsed_time:.2f}s\")\n",
|
||||
" print(f\"📊 Actual FPS: {actual_fps:.2f}\")\n",
|
||||
" \n",
|
||||
" return video_filename\n",
|
||||
"\n",
|
||||
"# Test recording (change camera_id as needed)\n",
|
||||
"if cameras: # Only test if cameras were found\n",
|
||||
" test_camera = cameras[0] # Use first available camera\n",
|
||||
" video_file = test_video_recording(test_camera, duration_seconds=3)\n",
|
||||
"else:\n",
|
||||
" print(\"⚠️ No cameras available for video test\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "USDA-vision-cameras",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "ba958c88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# coding=utf-8\n",
|
||||
"\"\"\"\n",
|
||||
"Test script to help find optimal exposure settings for your GigE camera.\n",
|
||||
"This script captures a single test image with different exposure settings.\n",
|
||||
"\"\"\"\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"sys.path.append(\"./python demo\")\n",
|
||||
"import os\n",
|
||||
"import mvsdk\n",
|
||||
"import numpy as np\n",
|
||||
"import cv2\n",
|
||||
"import platform\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"# Add the python demo directory to path\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"id": "23f1dc49",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def test_exposure_settings():\n",
|
||||
" \"\"\"\n",
|
||||
" Test different exposure settings to find optimal values\n",
|
||||
" \"\"\"\n",
|
||||
" # Initialize SDK\n",
|
||||
" try:\n",
|
||||
" mvsdk.CameraSdkInit(1)\n",
|
||||
" print(\"SDK initialized successfully\")\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"SDK initialization failed: {e}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" # Enumerate cameras\n",
|
||||
" DevList = mvsdk.CameraEnumerateDevice()\n",
|
||||
" nDev = len(DevList)\n",
|
||||
"\n",
|
||||
" if nDev < 1:\n",
|
||||
" print(\"No camera was found!\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" print(f\"Found {nDev} camera(s):\")\n",
|
||||
" for i, DevInfo in enumerate(DevList):\n",
|
||||
" print(f\" {i}: {DevInfo.GetFriendlyName()} ({DevInfo.GetPortType()})\")\n",
|
||||
"\n",
|
||||
" # Use first camera\n",
|
||||
" DevInfo = DevList[0]\n",
|
||||
" print(f\"\\nSelected camera: {DevInfo.GetFriendlyName()}\")\n",
|
||||
"\n",
|
||||
" # Initialize camera\n",
|
||||
" try:\n",
|
||||
" hCamera = mvsdk.CameraInit(DevInfo, -1, -1)\n",
|
||||
" print(\"Camera initialized successfully\")\n",
|
||||
" except mvsdk.CameraException as e:\n",
|
||||
" print(f\"CameraInit Failed({e.error_code}): {e.message}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" try:\n",
|
||||
" # Get camera capabilities\n",
|
||||
" cap = mvsdk.CameraGetCapability(hCamera)\n",
|
||||
" monoCamera = cap.sIspCapacity.bMonoSensor != 0\n",
|
||||
" print(f\"Camera type: {'Monochrome' if monoCamera else 'Color'}\")\n",
|
||||
"\n",
|
||||
" # Get camera ranges\n",
|
||||
" try:\n",
|
||||
" exp_min, exp_max, exp_step = mvsdk.CameraGetExposureTimeRange(hCamera)\n",
|
||||
" print(f\"Exposure time range: {exp_min:.1f} - {exp_max:.1f} μs\")\n",
|
||||
"\n",
|
||||
" gain_min, gain_max, gain_step = mvsdk.CameraGetAnalogGainXRange(hCamera)\n",
|
||||
" print(f\"Analog gain range: {gain_min:.2f} - {gain_max:.2f}x\")\n",
|
||||
"\n",
|
||||
" print(\"whatever this is: \", mvsdk.CameraGetAnalogGainXRange(hCamera))\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Could not get camera ranges: {e}\")\n",
|
||||
" exp_min, exp_max = 100, 100000\n",
|
||||
" gain_min, gain_max = 1.0, 4.0\n",
|
||||
"\n",
|
||||
" # Set output format\n",
|
||||
" if monoCamera:\n",
|
||||
" mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_MONO8)\n",
|
||||
" else:\n",
|
||||
" mvsdk.CameraSetIspOutFormat(hCamera, mvsdk.CAMERA_MEDIA_TYPE_BGR8)\n",
|
||||
"\n",
|
||||
" # Set camera to continuous capture mode\n",
|
||||
" mvsdk.CameraSetTriggerMode(hCamera, 0)\n",
|
||||
" mvsdk.CameraSetAeState(hCamera, 0) # Disable auto exposure\n",
|
||||
"\n",
|
||||
" # Start camera\n",
|
||||
" mvsdk.CameraPlay(hCamera)\n",
|
||||
"\n",
|
||||
" # Allocate frame buffer\n",
|
||||
" FrameBufferSize = cap.sResolutionRange.iWidthMax * cap.sResolutionRange.iHeightMax * (1 if monoCamera else 3)\n",
|
||||
" pFrameBuffer = mvsdk.CameraAlignMalloc(FrameBufferSize, 16)\n",
|
||||
"\n",
|
||||
" # Create test directory\n",
|
||||
" if not os.path.exists(\"exposure_tests\"):\n",
|
||||
" os.makedirs(\"exposure_tests\")\n",
|
||||
"\n",
|
||||
" print(\"\\nTesting different exposure settings...\")\n",
|
||||
" print(\"=\" * 50)\n",
|
||||
"\n",
|
||||
" # Test different exposure times (in microseconds)\n",
|
||||
" exposure_times = [100, 200, 500, 1000, 2000, 5000, 10000, 20000] # 0.5ms to 20ms\n",
|
||||
" analog_gains = [2.5, 5.0, 10.0, 16.0] # Start with 1x gain\n",
|
||||
"\n",
|
||||
" test_count = 0\n",
|
||||
" for exp_time in exposure_times:\n",
|
||||
" for gain in analog_gains:\n",
|
||||
" # Clamp values to valid ranges\n",
|
||||
" exp_time = max(exp_min, min(exp_max, exp_time))\n",
|
||||
" gain = max(gain_min, min(gain_max, gain))\n",
|
||||
"\n",
|
||||
" print(f\"\\nTest {test_count + 1}: Exposure={exp_time/1000:.1f}ms, Gain={gain:.1f}x\")\n",
|
||||
"\n",
|
||||
" # Set camera parameters\n",
|
||||
" mvsdk.CameraSetExposureTime(hCamera, exp_time)\n",
|
||||
" try:\n",
|
||||
" mvsdk.CameraSetAnalogGainX(hCamera, gain)\n",
|
||||
" except:\n",
|
||||
" pass # Some cameras might not support this\n",
|
||||
"\n",
|
||||
" # Wait a moment for settings to take effect\n",
|
||||
" import time\n",
|
||||
"\n",
|
||||
" time.sleep(0.1)\n",
|
||||
"\n",
|
||||
" # Capture image\n",
|
||||
" try:\n",
|
||||
" pRawData, FrameHead = mvsdk.CameraGetImageBuffer(hCamera, 2000)\n",
|
||||
" mvsdk.CameraImageProcess(hCamera, pRawData, pFrameBuffer, FrameHead)\n",
|
||||
" mvsdk.CameraReleaseImageBuffer(hCamera, pRawData)\n",
|
||||
"\n",
|
||||
" # Handle Windows image flip\n",
|
||||
" if platform.system() == \"Windows\":\n",
|
||||
" mvsdk.CameraFlipFrameBuffer(pFrameBuffer, FrameHead, 1)\n",
|
||||
"\n",
|
||||
" # Convert to numpy array\n",
|
||||
" frame_data = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(pFrameBuffer)\n",
|
||||
" frame = np.frombuffer(frame_data, dtype=np.uint8)\n",
|
||||
"\n",
|
||||
" if FrameHead.uiMediaType == mvsdk.CAMERA_MEDIA_TYPE_MONO8:\n",
|
||||
" frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth))\n",
|
||||
" else:\n",
|
||||
" frame = frame.reshape((FrameHead.iHeight, FrameHead.iWidth, 3))\n",
|
||||
"\n",
|
||||
" # Calculate image statistics\n",
|
||||
" mean_brightness = np.mean(frame)\n",
|
||||
" max_brightness = np.max(frame)\n",
|
||||
"\n",
|
||||
" # Save image\n",
|
||||
" filename = f\"exposure_tests/test_{test_count+1:02d}_exp{exp_time/1000:.1f}ms_gain{gain:.1f}x.jpg\"\n",
|
||||
" cv2.imwrite(filename, frame)\n",
|
||||
"\n",
|
||||
" # Provide feedback\n",
|
||||
" status = \"\"\n",
|
||||
" if mean_brightness < 50:\n",
|
||||
" status = \"TOO DARK\"\n",
|
||||
" elif mean_brightness > 200:\n",
|
||||
" status = \"TOO BRIGHT\"\n",
|
||||
" elif max_brightness >= 255:\n",
|
||||
" status = \"OVEREXPOSED\"\n",
|
||||
" else:\n",
|
||||
" status = \"GOOD\"\n",
|
||||
"\n",
|
||||
" print(f\" → Saved: {filename}\")\n",
|
||||
" print(f\" → Brightness: mean={mean_brightness:.1f}, max={max_brightness:.1f} [{status}]\")\n",
|
||||
"\n",
|
||||
" test_count += 1\n",
|
||||
"\n",
|
||||
" except mvsdk.CameraException as e:\n",
|
||||
" print(f\" → Failed to capture: {e.message}\")\n",
|
||||
"\n",
|
||||
" print(f\"\\nCompleted {test_count} test captures!\")\n",
|
||||
" print(\"Check the 'exposure_tests' directory to see the results.\")\n",
|
||||
" print(\"\\nRecommendations:\")\n",
|
||||
" print(\"- Look for images marked as 'GOOD' - these have optimal exposure\")\n",
|
||||
" print(\"- If all images are 'TOO BRIGHT', try lower exposure times or gains\")\n",
|
||||
" print(\"- If all images are 'TOO DARK', try higher exposure times or gains\")\n",
|
||||
" print(\"- Avoid 'OVEREXPOSED' images as they have clipped highlights\")\n",
|
||||
"\n",
|
||||
" # Cleanup\n",
|
||||
" mvsdk.CameraAlignFree(pFrameBuffer)\n",
|
||||
"\n",
|
||||
" finally:\n",
|
||||
" # Close camera\n",
|
||||
" mvsdk.CameraUnInit(hCamera)\n",
|
||||
" print(\"\\nCamera closed\")\n",
|
||||
"\n",
|
||||
" return True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"id": "2891b5bf",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"GigE Camera Exposure Test Script\n",
|
||||
"========================================\n",
|
||||
"This script will test different exposure settings and save sample images.\n",
|
||||
"Use this to find the optimal settings for your lighting conditions.\n",
|
||||
"\n",
|
||||
"SDK initialized successfully\n",
|
||||
"Found 2 camera(s):\n",
|
||||
" 0: Blower-Yield-Cam (NET-100M-192.168.1.204)\n",
|
||||
" 1: Cracker-Cam (NET-1000M-192.168.1.246)\n",
|
||||
"\n",
|
||||
"Selected camera: Blower-Yield-Cam\n",
|
||||
"Camera initialized successfully\n",
|
||||
"Camera type: Color\n",
|
||||
"Exposure time range: 8.0 - 1048568.0 μs\n",
|
||||
"Analog gain range: 2.50 - 16.50x\n",
|
||||
"whatever this is: (2.5, 16.5, 0.5)\n",
|
||||
"\n",
|
||||
"Testing different exposure settings...\n",
|
||||
"==================================================\n",
|
||||
"\n",
|
||||
"Test 1: Exposure=0.1ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_01_exp0.1ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=94.1, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 2: Exposure=0.1ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_02_exp0.1ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=13.7, max=173.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 3: Exposure=0.1ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_03_exp0.1ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=14.1, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 4: Exposure=0.1ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_04_exp0.1ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=18.2, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 5: Exposure=0.2ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_05_exp0.2ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=22.1, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 6: Exposure=0.2ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_06_exp0.2ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=19.5, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 7: Exposure=0.2ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_07_exp0.2ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=25.3, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 8: Exposure=0.2ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_08_exp0.2ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=36.6, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 9: Exposure=0.5ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_09_exp0.5ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=55.8, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 10: Exposure=0.5ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_10_exp0.5ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=38.5, max=255.0 [TOO DARK]\n",
|
||||
"\n",
|
||||
"Test 11: Exposure=0.5ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_11_exp0.5ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=60.2, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 12: Exposure=0.5ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_12_exp0.5ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=99.3, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 13: Exposure=1.0ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_13_exp1.0ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=121.1, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 14: Exposure=1.0ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_14_exp1.0ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=68.8, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 15: Exposure=1.0ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_15_exp1.0ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=109.6, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 16: Exposure=1.0ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_16_exp1.0ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=148.7, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 17: Exposure=2.0ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_17_exp2.0ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=171.9, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 18: Exposure=2.0ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_18_exp2.0ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=117.9, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 19: Exposure=2.0ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_19_exp2.0ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=159.0, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 20: Exposure=2.0ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_20_exp2.0ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=195.7, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 21: Exposure=5.0ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_21_exp5.0ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=214.6, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 22: Exposure=5.0ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_22_exp5.0ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=180.2, max=255.0 [OVEREXPOSED]\n",
|
||||
"\n",
|
||||
"Test 23: Exposure=5.0ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_23_exp5.0ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=214.6, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 24: Exposure=5.0ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_24_exp5.0ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=239.6, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 25: Exposure=10.0ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_25_exp10.0ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=247.5, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 26: Exposure=10.0ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_26_exp10.0ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=252.4, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 27: Exposure=10.0ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_27_exp10.0ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=218.9, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 28: Exposure=10.0ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_28_exp10.0ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=250.8, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 29: Exposure=20.0ms, Gain=2.5x\n",
|
||||
" → Saved: exposure_tests/test_29_exp20.0ms_gain2.5x.jpg\n",
|
||||
" → Brightness: mean=252.4, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 30: Exposure=20.0ms, Gain=5.0x\n",
|
||||
" → Saved: exposure_tests/test_30_exp20.0ms_gain5.0x.jpg\n",
|
||||
" → Brightness: mean=244.4, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 31: Exposure=20.0ms, Gain=10.0x\n",
|
||||
" → Saved: exposure_tests/test_31_exp20.0ms_gain10.0x.jpg\n",
|
||||
" → Brightness: mean=251.5, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Test 32: Exposure=20.0ms, Gain=16.0x\n",
|
||||
" → Saved: exposure_tests/test_32_exp20.0ms_gain16.0x.jpg\n",
|
||||
" → Brightness: mean=253.4, max=255.0 [TOO BRIGHT]\n",
|
||||
"\n",
|
||||
"Completed 32 test captures!\n",
|
||||
"Check the 'exposure_tests' directory to see the results.\n",
|
||||
"\n",
|
||||
"Recommendations:\n",
|
||||
"- Look for images marked as 'GOOD' - these have optimal exposure\n",
|
||||
"- If all images are 'TOO BRIGHT', try lower exposure times or gains\n",
|
||||
"- If all images are 'TOO DARK', try higher exposure times or gains\n",
|
||||
"- Avoid 'OVEREXPOSED' images as they have clipped highlights\n",
|
||||
"\n",
|
||||
"Camera closed\n",
|
||||
"\n",
|
||||
"Testing completed successfully!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" print(\"GigE Camera Exposure Test Script\")\n",
|
||||
" print(\"=\" * 40)\n",
|
||||
" print(\"This script will test different exposure settings and save sample images.\")\n",
|
||||
" print(\"Use this to find the optimal settings for your lighting conditions.\")\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
" success = test_exposure_settings()\n",
|
||||
"\n",
|
||||
" if success:\n",
|
||||
" print(\"\\nTesting completed successfully!\")\n",
|
||||
" else:\n",
|
||||
" print(\"\\nTesting failed!\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ead8d889",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "cc_pecan",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Advanced GigE Camera Configuration\n",
|
||||
"\n",
|
||||
"This notebook provides advanced testing and configuration for GigE cameras.\n",
|
||||
"\n",
|
||||
"## Features:\n",
|
||||
"- Network interface detection\n",
|
||||
"- GigE camera discovery\n",
|
||||
"- Camera parameter configuration\n",
|
||||
"- Performance testing\n",
|
||||
"- Dual camera synchronization testing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import cv2\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import subprocess\n",
|
||||
"import socket\n",
|
||||
"import threading\n",
|
||||
"import time\n",
|
||||
"from datetime import datetime\n",
|
||||
"import os\n",
|
||||
"from pathlib import Path\n",
|
||||
"import json\n",
|
||||
"\n",
|
||||
"print(\"✅ Imports successful!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Network Interface Detection"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_network_interfaces():\n",
|
||||
" \"\"\"Get network interface information\"\"\"\n",
|
||||
" try:\n",
|
||||
" result = subprocess.run(['ip', 'addr', 'show'], capture_output=True, text=True)\n",
|
||||
" print(\"🌐 Network Interfaces:\")\n",
|
||||
" print(result.stdout)\n",
|
||||
" \n",
|
||||
" # Also check for GigE specific interfaces\n",
|
||||
" result2 = subprocess.run(['ifconfig'], capture_output=True, text=True)\n",
|
||||
" if result2.returncode == 0:\n",
|
||||
" print(\"\\n📡 Interface Configuration:\")\n",
|
||||
" print(result2.stdout)\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"❌ Error getting network info: {e}\")\n",
|
||||
"\n",
|
||||
"get_network_interfaces()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## GigE Camera Discovery"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def discover_gige_cameras():\n",
|
||||
" \"\"\"Attempt to discover GigE cameras on the network\"\"\"\n",
|
||||
" print(\"🔍 Discovering GigE cameras...\")\n",
|
||||
" \n",
|
||||
" # Try different methods to find GigE cameras\n",
|
||||
" methods = [\n",
|
||||
" \"OpenCV with different backends\",\n",
|
||||
" \"Network scanning\",\n",
|
||||
" \"GStreamer pipeline testing\"\n",
|
||||
" ]\n",
|
||||
" \n",
|
||||
" print(\"\\n1. Testing OpenCV backends:\")\n",
|
||||
" backends = [\n",
|
||||
" (cv2.CAP_GSTREAMER, \"GStreamer\"),\n",
|
||||
" (cv2.CAP_V4L2, \"V4L2\"),\n",
|
||||
" (cv2.CAP_FFMPEG, \"FFmpeg\"),\n",
|
||||
" (cv2.CAP_ANY, \"Default\")\n",
|
||||
" ]\n",
|
||||
" \n",
|
||||
" for backend_id, backend_name in backends:\n",
|
||||
" print(f\" Testing {backend_name}...\")\n",
|
||||
" for cam_id in range(5):\n",
|
||||
" try:\n",
|
||||
" cap = cv2.VideoCapture(cam_id, backend_id)\n",
|
||||
" if cap.isOpened():\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" print(f\" ✅ Camera {cam_id} accessible via {backend_name}\")\n",
|
||||
" print(f\" Resolution: {frame.shape[1]}x{frame.shape[0]}\")\n",
|
||||
" cap.release()\n",
|
||||
" except Exception as e:\n",
|
||||
" pass\n",
|
||||
" \n",
|
||||
" print(\"\\n2. Testing GStreamer pipelines:\")\n",
|
||||
" # Common GigE camera GStreamer pipelines\n",
|
||||
" gstreamer_pipelines = [\n",
|
||||
" \"v4l2src device=/dev/video0 ! videoconvert ! appsink\",\n",
|
||||
" \"v4l2src device=/dev/video1 ! videoconvert ! appsink\",\n",
|
||||
" \"tcambin ! videoconvert ! appsink\", # For TIS cameras\n",
|
||||
" \"aravis ! videoconvert ! appsink\", # For Aravis-supported cameras\n",
|
||||
" ]\n",
|
||||
" \n",
|
||||
" for pipeline in gstreamer_pipelines:\n",
|
||||
" try:\n",
|
||||
" print(f\" Testing: {pipeline}\")\n",
|
||||
" cap = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)\n",
|
||||
" if cap.isOpened():\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" print(f\" ✅ Pipeline works! Frame shape: {frame.shape}\")\n",
|
||||
" else:\n",
|
||||
" print(f\" ⚠️ Pipeline opened but no frames\")\n",
|
||||
" else:\n",
|
||||
" print(f\" ❌ Pipeline failed\")\n",
|
||||
" cap.release()\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\" ❌ Error: {e}\")\n",
|
||||
"\n",
|
||||
"discover_gige_cameras()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Camera Parameter Configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def configure_camera_parameters(camera_id, backend=cv2.CAP_ANY):\n",
|
||||
" \"\"\"Configure and test camera parameters\"\"\"\n",
|
||||
" print(f\"⚙️ Configuring camera {camera_id}...\")\n",
|
||||
" \n",
|
||||
" cap = cv2.VideoCapture(camera_id, backend)\n",
|
||||
" if not cap.isOpened():\n",
|
||||
" print(f\"❌ Cannot open camera {camera_id}\")\n",
|
||||
" return None\n",
|
||||
" \n",
|
||||
" # Get current parameters\n",
|
||||
" current_params = {\n",
|
||||
" 'width': cap.get(cv2.CAP_PROP_FRAME_WIDTH),\n",
|
||||
" 'height': cap.get(cv2.CAP_PROP_FRAME_HEIGHT),\n",
|
||||
" 'fps': cap.get(cv2.CAP_PROP_FPS),\n",
|
||||
" 'brightness': cap.get(cv2.CAP_PROP_BRIGHTNESS),\n",
|
||||
" 'contrast': cap.get(cv2.CAP_PROP_CONTRAST),\n",
|
||||
" 'saturation': cap.get(cv2.CAP_PROP_SATURATION),\n",
|
||||
" 'hue': cap.get(cv2.CAP_PROP_HUE),\n",
|
||||
" 'gain': cap.get(cv2.CAP_PROP_GAIN),\n",
|
||||
" 'exposure': cap.get(cv2.CAP_PROP_EXPOSURE),\n",
|
||||
" 'auto_exposure': cap.get(cv2.CAP_PROP_AUTO_EXPOSURE),\n",
|
||||
" 'white_balance': cap.get(cv2.CAP_PROP_WHITE_BALANCE_BLUE_U),\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" print(\"📊 Current Camera Parameters:\")\n",
|
||||
" for param, value in current_params.items():\n",
|
||||
" print(f\" {param}: {value}\")\n",
|
||||
" \n",
|
||||
" # Test setting some parameters\n",
|
||||
" print(\"\\n🔧 Testing parameter changes:\")\n",
|
||||
" \n",
|
||||
" # Try to set resolution (common GigE resolutions)\n",
|
||||
" test_resolutions = [(1920, 1080), (1280, 720), (640, 480)]\n",
|
||||
" for width, height in test_resolutions:\n",
|
||||
" if cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) and cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height):\n",
|
||||
" actual_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)\n",
|
||||
" actual_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)\n",
|
||||
" print(f\" Resolution {width}x{height}: Set to {actual_width}x{actual_height}\")\n",
|
||||
" break\n",
|
||||
" \n",
|
||||
" # Test FPS settings\n",
|
||||
" for fps in [30, 60, 120]:\n",
|
||||
" if cap.set(cv2.CAP_PROP_FPS, fps):\n",
|
||||
" actual_fps = cap.get(cv2.CAP_PROP_FPS)\n",
|
||||
" print(f\" FPS {fps}: Set to {actual_fps}\")\n",
|
||||
" break\n",
|
||||
" \n",
|
||||
" # Capture test frame with new settings\n",
|
||||
" ret, frame = cap.read()\n",
|
||||
" if ret:\n",
|
||||
" print(f\"\\n✅ Test frame captured: {frame.shape}\")\n",
|
||||
" \n",
|
||||
" # Display frame\n",
|
||||
" plt.figure(figsize=(10, 6))\n",
|
||||
" if len(frame.shape) == 3:\n",
|
||||
" plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))\n",
|
||||
" else:\n",
|
||||
" plt.imshow(frame, cmap='gray')\n",
|
||||
" plt.title(f\"Camera {camera_id} - Configured\")\n",
|
||||
" plt.axis('off')\n",
|
||||
" plt.show()\n",
|
||||
" \n",
|
||||
" # Save configuration and test image\n",
|
||||
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" \n",
|
||||
" # Save image\n",
|
||||
" img_path = f\"/storage/camera{camera_id}/configured_test_{timestamp}.jpg\"\n",
|
||||
" cv2.imwrite(img_path, frame)\n",
|
||||
" print(f\"💾 Test image saved: {img_path}\")\n",
|
||||
" \n",
|
||||
" # Save configuration\n",
|
||||
" config_path = f\"/storage/camera{camera_id}/config_{timestamp}.json\"\n",
|
||||
" with open(config_path, 'w') as f:\n",
|
||||
" json.dump(current_params, f, indent=2)\n",
|
||||
" print(f\"💾 Configuration saved: {config_path}\")\n",
|
||||
" \n",
|
||||
" cap.release()\n",
|
||||
" return current_params\n",
|
||||
"\n",
|
||||
"# Test configuration (change camera_id as needed)\n",
|
||||
"camera_to_configure = 0\n",
|
||||
"config = configure_camera_parameters(camera_to_configure)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dual Camera Testing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def test_dual_cameras(camera1_id=0, camera2_id=1, duration=5):\n",
|
||||
" \"\"\"Test simultaneous capture from two cameras\"\"\"\n",
|
||||
" print(f\"📷📷 Testing dual camera capture (cameras {camera1_id} and {camera2_id})...\")\n",
|
||||
" \n",
|
||||
" # Open both cameras\n",
|
||||
" cap1 = cv2.VideoCapture(camera1_id)\n",
|
||||
" cap2 = cv2.VideoCapture(camera2_id)\n",
|
||||
" \n",
|
||||
" if not cap1.isOpened():\n",
|
||||
" print(f\"❌ Cannot open camera {camera1_id}\")\n",
|
||||
" return\n",
|
||||
" \n",
|
||||
" if not cap2.isOpened():\n",
|
||||
" print(f\"❌ Cannot open camera {camera2_id}\")\n",
|
||||
" cap1.release()\n",
|
||||
" return\n",
|
||||
" \n",
|
||||
" print(\"✅ Both cameras opened successfully\")\n",
|
||||
" \n",
|
||||
" # Capture test frames\n",
|
||||
" ret1, frame1 = cap1.read()\n",
|
||||
" ret2, frame2 = cap2.read()\n",
|
||||
" \n",
|
||||
" if ret1 and ret2:\n",
|
||||
" print(f\"📊 Camera {camera1_id}: {frame1.shape}\")\n",
|
||||
" print(f\"📊 Camera {camera2_id}: {frame2.shape}\")\n",
|
||||
" \n",
|
||||
" # Display both frames side by side\n",
|
||||
" fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))\n",
|
||||
" \n",
|
||||
" if len(frame1.shape) == 3:\n",
|
||||
" ax1.imshow(cv2.cvtColor(frame1, cv2.COLOR_BGR2RGB))\n",
|
||||
" else:\n",
|
||||
" ax1.imshow(frame1, cmap='gray')\n",
|
||||
" ax1.set_title(f\"Camera {camera1_id}\")\n",
|
||||
" ax1.axis('off')\n",
|
||||
" \n",
|
||||
" if len(frame2.shape) == 3:\n",
|
||||
" ax2.imshow(cv2.cvtColor(frame2, cv2.COLOR_BGR2RGB))\n",
|
||||
" else:\n",
|
||||
" ax2.imshow(frame2, cmap='gray')\n",
|
||||
" ax2.set_title(f\"Camera {camera2_id}\")\n",
|
||||
" ax2.axis('off')\n",
|
||||
" \n",
|
||||
" plt.tight_layout()\n",
|
||||
" plt.show()\n",
|
||||
" \n",
|
||||
" # Save test images\n",
|
||||
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" cv2.imwrite(f\"/storage/camera1/dual_test_{timestamp}.jpg\", frame1)\n",
|
||||
" cv2.imwrite(f\"/storage/camera2/dual_test_{timestamp}.jpg\", frame2)\n",
|
||||
" print(f\"💾 Dual camera test images saved with timestamp {timestamp}\")\n",
|
||||
" \n",
|
||||
" else:\n",
|
||||
" print(\"❌ Failed to capture from one or both cameras\")\n",
|
||||
" \n",
|
||||
" # Test synchronized recording\n",
|
||||
" print(f\"\\n🎥 Testing synchronized recording for {duration} seconds...\")\n",
|
||||
" \n",
|
||||
" # Setup video writers\n",
|
||||
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" \n",
|
||||
" fourcc = cv2.VideoWriter_fourcc(*'mp4v')\n",
|
||||
" fps = 30\n",
|
||||
" \n",
|
||||
" if ret1:\n",
|
||||
" h1, w1 = frame1.shape[:2]\n",
|
||||
" out1 = cv2.VideoWriter(f\"/storage/camera1/sync_test_{timestamp}.mp4\", fourcc, fps, (w1, h1))\n",
|
||||
" \n",
|
||||
" if ret2:\n",
|
||||
" h2, w2 = frame2.shape[:2]\n",
|
||||
" out2 = cv2.VideoWriter(f\"/storage/camera2/sync_test_{timestamp}.mp4\", fourcc, fps, (w2, h2))\n",
|
||||
" \n",
|
||||
" # Record synchronized video\n",
|
||||
" start_time = time.time()\n",
|
||||
" frame_count = 0\n",
|
||||
" \n",
|
||||
" while time.time() - start_time < duration:\n",
|
||||
" ret1, frame1 = cap1.read()\n",
|
||||
" ret2, frame2 = cap2.read()\n",
|
||||
" \n",
|
||||
" if ret1 and ret2:\n",
|
||||
" out1.write(frame1)\n",
|
||||
" out2.write(frame2)\n",
|
||||
" frame_count += 1\n",
|
||||
" else:\n",
|
||||
" print(f\"⚠️ Frame drop at frame {frame_count}\")\n",
|
||||
" \n",
|
||||
" # Cleanup\n",
|
||||
" cap1.release()\n",
|
||||
" cap2.release()\n",
|
||||
" if 'out1' in locals():\n",
|
||||
" out1.release()\n",
|
||||
" if 'out2' in locals():\n",
|
||||
" out2.release()\n",
|
||||
" \n",
|
||||
" elapsed = time.time() - start_time\n",
|
||||
" actual_fps = frame_count / elapsed\n",
|
||||
" \n",
|
||||
" print(f\"✅ Synchronized recording complete\")\n",
|
||||
" print(f\"📊 Recorded {frame_count} frames in {elapsed:.2f}s\")\n",
|
||||
" print(f\"📊 Actual FPS: {actual_fps:.2f}\")\n",
|
||||
" print(f\"💾 Videos saved with timestamp {timestamp}\")\n",
|
||||
"\n",
|
||||
"# Test dual cameras (adjust camera IDs as needed)\n",
|
||||
"test_dual_cameras(0, 1, duration=3)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "usda-vision-cameras",
|
||||
"language": "python",
|
||||
"name": "usda-vision-cameras"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "3b92c632",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import paho.mqtt.client as mqtt\n",
|
||||
"import time\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "a6753fb1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/tmp/ipykernel_2342/243927247.py:34: DeprecationWarning: Callback API version 1 is deprecated, update to latest version\n",
|
||||
" client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1) # Use VERSION1 for broader compatibility\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Connecting to MQTT broker at 192.168.1.110:1883...\n",
|
||||
"Successfully connected to MQTT Broker!\n",
|
||||
"Subscribed to topic: 'vision/vibratory_conveyor/state'\n",
|
||||
"Listening for messages... (Press Ctrl+C to stop)\n",
|
||||
"\n",
|
||||
"--- MQTT MESSAGE RECEIVED! ---\n",
|
||||
" Topic: vision/vibratory_conveyor/state\n",
|
||||
" Payload: on\n",
|
||||
" Time: 2025-07-25 21:03:21\n",
|
||||
"------------------------------\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"--- MQTT MESSAGE RECEIVED! ---\n",
|
||||
" Topic: vision/vibratory_conveyor/state\n",
|
||||
" Payload: off\n",
|
||||
" Time: 2025-07-25 21:05:26\n",
|
||||
"------------------------------\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Stopping MQTT listener.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\n",
|
||||
"# --- MQTT Broker Configuration ---\n",
|
||||
"# Your Home Assistant's IP address (where your MQTT broker is running)\n",
|
||||
"MQTT_BROKER_HOST = \"192.168.1.110\"\n",
|
||||
"MQTT_BROKER_PORT = 1883\n",
|
||||
"# IMPORTANT: Replace with your actual MQTT broker username and password if you have one set up\n",
|
||||
"# (These are NOT your Home Assistant login credentials, but for the Mosquitto add-on, if used)\n",
|
||||
"# MQTT_BROKER_USERNAME = \"pecan\" # e.g., \"homeassistant_mqtt_user\"\n",
|
||||
"# MQTT_BROKER_PASSWORD = \"whatever\" # e.g., \"SuperSecurePassword123!\"\n",
|
||||
"\n",
|
||||
"# --- Topic to Subscribe To ---\n",
|
||||
"# This MUST exactly match the topic you set in your Home Assistant automation\n",
|
||||
"MQTT_TOPIC = \"vision/vibratory_conveyor/state\" # <<<< Make sure this is correct!\n",
|
||||
"MQTT_TOPIC = \"vision/blower_separator/state\" # <<<< Make sure this is correct!\n",
|
||||
"\n",
|
||||
"# The callback for when the client receives a CONNACK response from the server.\n",
|
||||
"def on_connect(client, userdata, flags, rc):\n",
|
||||
" if rc == 0:\n",
|
||||
" print(\"Successfully connected to MQTT Broker!\")\n",
|
||||
" client.subscribe(MQTT_TOPIC)\n",
|
||||
" print(f\"Subscribed to topic: '{MQTT_TOPIC}'\")\n",
|
||||
" print(\"Listening for messages... (Press Ctrl+C to stop)\")\n",
|
||||
" else:\n",
|
||||
" print(f\"Failed to connect, return code {rc}\\n\")\n",
|
||||
"\n",
|
||||
"# The callback for when a PUBLISH message is received from the server.\n",
|
||||
"def on_message(client, userdata, msg):\n",
|
||||
" received_payload = msg.payload.decode()\n",
|
||||
" print(f\"\\n--- MQTT MESSAGE RECEIVED! ---\")\n",
|
||||
" print(f\" Topic: {msg.topic}\")\n",
|
||||
" print(f\" Payload: {received_payload}\")\n",
|
||||
" print(f\" Time: {time.strftime('%Y-%m-%d %H:%M:%S')}\")\n",
|
||||
" print(f\"------------------------------\\n\")\n",
|
||||
"\n",
|
||||
"# Create an MQTT client instance\n",
|
||||
"client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1) # Use VERSION1 for broader compatibility\n",
|
||||
"\n",
|
||||
"# Set callback functions\n",
|
||||
"client.on_connect = on_connect\n",
|
||||
"client.on_message = on_message\n",
|
||||
"\n",
|
||||
"# Set username and password if required\n",
|
||||
"# (Only uncomment and fill these if your MQTT broker requires authentication)\n",
|
||||
"# client.username_pw_set(MQTT_BROKER_USERNAME, MQTT_BROKER_PASSWORD)\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" # Attempt to connect to the MQTT broker\n",
|
||||
" print(f\"Connecting to MQTT broker at {MQTT_BROKER_HOST}:{MQTT_BROKER_PORT}...\")\n",
|
||||
" client.connect(MQTT_BROKER_HOST, MQTT_BROKER_PORT, 60)\n",
|
||||
"\n",
|
||||
" # Start the MQTT loop. This runs in the background and processes messages.\n",
|
||||
" client.loop_forever()\n",
|
||||
"\n",
|
||||
"except KeyboardInterrupt:\n",
|
||||
" print(\"\\nStopping MQTT listener.\")\n",
|
||||
" client.disconnect() # Disconnect gracefully\n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"An unexpected error occurred: {e}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "56531671",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "USDA-vision-cameras",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user