pulse-monitor/test_rotation.go

231 lines
7.1 KiB
Go

//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.")
}