231 lines
7.1 KiB
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.")
|
|
}
|