//go:build ignore // +build ignore // This is a standalone test program for rotation detection. // Run with: go run test_rotation.go detection.go package main import ( "fmt" "log" "os" "gocv.io/x/gocv" ) // DEBUG flag for this test program (separate from main build) // Note: When running this test, set DEBUG = true to enable debug output in detection.go var DEBUG = false func main() { // Parse command line args if len(os.Args) > 1 && os.Args[1] == "/debug" { DEBUG = true fmt.Println("DEBUG mode enabled\n") } // Hardcode RTSP URL streamURL := "rtsp://tapohass:!!Helder06@192.168.2.183:554/stream1" fmt.Println("Rotation Detection System") fmt.Println("========================") fmt.Println("Processing frames...\n") // Open RTSP stream stream, err := gocv.OpenVideoCapture(streamURL) if err != nil { log.Fatalf("Failed to open stream: %v", err) } defer stream.Close() // Initialize detection state rotation := 9999.0 // Sentinel value for "not detected" width := 0 // 0 means "not detected" scaleFactor := 0.0 // Scale factor from detection // Frame processing counters framesSkipped := 0 framesProcessed := 0 detectionAttempts := 0 totalFramesRead := 0 SKIP_COUNT := 7 // Process every 8th frame MAX_DETECTION_ATTEMPTS := 100 // Give up after 100 attempts RE_DETECT_AFTER := 0 // Re-detect after N frames (0 = disabled) // Main processing loop frame := gocv.NewMat() defer frame.Close() for { // Acquire a frame if ok := stream.Read(&frame); !ok || frame.Empty() { log.Fatal("Failed to read frame from stream") } totalFramesRead++ // Preprocess frame to binary binary := PreprocessFrame(frame) // If rotation is 9999 or width is 0, detect bands/width/rotation if rotation == 9999 || width == 0 || scaleFactor == 0.0 { detectionAttempts++ // Give up if too many attempts if detectionAttempts > MAX_DETECTION_ATTEMPTS { fmt.Printf("❌ Detection failed after %d attempts. Exiting.\n", MAX_DETECTION_ATTEMPTS) break } result := DetectRotationAndWidth(binary) if result.Success { rotation = result.Rotation width = result.Width scaleFactor = result.ScaleFactor fmt.Printf("✓ DETECTION SUCCESSFUL (frame %d, attempt #%d):\n", totalFramesRead, detectionAttempts) fmt.Printf(" Width=%dpx, Rotation=%.3f°, Scale=%.3f\n\n", width, rotation, scaleFactor) detectionAttempts = 0 // Reset counter } else { // Show detection attempts with frame info if detectionAttempts <= 5 { if DEBUG { fmt.Printf("Frame %d: Detection attempt #%d failed\n", totalFramesRead, detectionAttempts) } } else { fmt.Printf("⚠ Frame %d: Detection attempt #%d failed - retrying...\n", totalFramesRead, detectionAttempts) } // Clean up and continue to next frame binary.Close() // Skip a few frames to ensure display has changed for skip := 0; skip < 3; skip++ { if ok := stream.Read(&frame); !ok { break } } continue } } // If rotation != 9999 and width != 0 and scaleFactor != 0.0 and we've skipped enough frames if rotation != 9999 && width != 0 && scaleFactor != 0.0 && framesSkipped >= SKIP_COUNT { framesProcessed++ // Apply the same rotation correction to this frame // (rotation was detected on a previous frame, now apply it to this one) rotated := RotateImage(binary, rotation) defer rotated.Close() // Save rotated frame BEFORE scaling if DEBUG && framesProcessed == 1 { gocv.IMWrite("debug_frame_001_AFTER_ROTATION_BEFORE_SCALE.png", rotated) fmt.Println("DEBUG: Saved rotated frame before scaling") } // Apply the same scaling using detected scale factor // This scales the entire frame so the baseline becomes 860px scaled := ScaleByFactor(rotated, scaleFactor) defer scaled.Close() fmt.Printf("Frame #%d processed: Rotated %.3f°, Scaled to %dx%d (factor: %.3f, baseline: %dpx -> 860px)\n", framesProcessed, rotation, scaled.Cols(), scaled.Rows(), scaleFactor, width) // Re-detect bands on scaled image for visualization if DEBUG && framesProcessed <= 5 { // Use the detected scale factor for minimum height scaledMinHeight := int(80.0 * scaleFactor) bands := DetectBands(scaled, scaledMinHeight) if len(bands) >= 2 { // Find tallest band (graph) tallestIdx := 0 tallestHeight := 0 for i, band := range bands { height := band.maxY - band.minY if height > tallestHeight { tallestHeight = height tallestIdx = i } } if tallestIdx < len(bands)-1 { graphBand := bands[tallestIdx] digitBand := bands[tallestIdx+1] // Extract digit region digitRegion := ExtractBandRegion(scaled, digitBand) defer digitRegion.Close() // Find digit boxes digitBoxes := FindDigitBoxes(digitRegion, scaledMinHeight) if DEBUG { fmt.Printf(" DEBUG: FindDigitBoxes returned %d boxes:\n", len(digitBoxes)) for i, box := range digitBoxes { fmt.Printf(" Box %d: X[%d-%d] Y[%d-%d] Size: %dx%d\n", i+1, box.Min.X, box.Max.X, box.Min.Y, box.Max.Y, box.Dx(), box.Dy()) } } // Calculate center from scaled baseline points // Use the same proportion as detected scaledCenterX := int(float64(scaled.Cols()) / 2.0) // Merge into displays displayAreas := mergeDigitBoxesWithCenter(digitRegion, digitBoxes, scaledCenterX) fmt.Println(" DEBUG: Saving visualizations...") // Visualize bands VisualizeSimpleBands(scaled, graphBand, digitBand, fmt.Sprintf("debug_frame_%03d_bands.png", framesProcessed)) // Save digit region gocv.IMWrite(fmt.Sprintf("debug_frame_%03d_digits.png", framesProcessed), digitRegion) // Visualize digit boxes VisualizeDigitBoxes(digitRegion, digitBoxes, digitBand.minY, fmt.Sprintf("debug_frame_%03d_digitboxes.png", framesProcessed)) // Visualize merged displays VisualizeDetectedDisplays(digitRegion, displayAreas, digitBoxes, scaledCenterX, fmt.Sprintf("debug_frame_%03d_merged.png", framesProcessed)) fmt.Printf(" DEBUG: Saved 4 visualization files for frame %d\n", framesProcessed) } } } // Save scaled frame if in DEBUG mode if DEBUG && framesProcessed <= 5 { filename := fmt.Sprintf("debug_frame_%03d_scaled.png", framesProcessed) gocv.IMWrite(filename, scaled) fmt.Printf("DEBUG: Saved %s\n", filename) } // TODO: Add OCR processing on 'scaled' image here in later phase // Display areas (SpO2 and HR) are already detected and stored in result // They will be available when we integrate this with the main monitoring system framesSkipped = 0 // Optional: Force re-detection after N frames to adapt to changes if RE_DETECT_AFTER > 0 && framesProcessed >= RE_DETECT_AFTER { fmt.Printf("\n🔄 Re-detecting after %d frames...\n", RE_DETECT_AFTER) rotation = 9999 width = 0 scaleFactor = 0.0 framesProcessed = 0 } } else { framesSkipped++ } // Clean up binary.Close() } fmt.Println("\nProgram terminated.") }