All files / web/src/app/api/vision-training/preview-augmentation route.ts

0% Statements 0/145
0% Branches 0/1
0% Functions 0/1
0% Lines 0/145

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146                                                                                                                                                                                                                                                                                                   
import { NextResponse } from 'next/server'
import { spawn } from 'child_process'
import path from 'path'
import { TRAINING_PYTHON, PYTHON_ENV, ensureVenvReady } from '../config'
import { withAuth } from '@/lib/auth/withAuth'

interface PipelineStepVariant {
  image_base64: string
  label: string
  description: string
}

interface PipelineStep {
  step: number
  name: string
  title: string
  description: string
  image_base64?: string
  variants?: PipelineStepVariant[]
  error?: string
  note?: string
  original_size?: string
  target_size?: string
}

interface PipelinePreviewResult {
  steps: PipelineStep[]
}

/**
 * Preview the preprocessing pipeline using the EXACT SAME Python code as training.
 *
 * Shows the image at each step:
 * 1. Raw image (loaded)
 * 2. Marker masking (ArUco markers obscured)
 * 3. Color augmentation (brightness/contrast/saturation variations)
 * 4. Resize (to 224x224)
 * 5. Normalize (to [0, 1] range)
 *
 * This ensures the preview matches exactly what goes through the training pipeline.
 */
export const POST = withAuth(
  async (request) => {
    try {
      const body = await request.json()
      const { imageData, corners, applyMasking = true, applyAugmentation = true } = body

      if (!imageData) {
        return NextResponse.json({ error: 'Missing imageData' }, { status: 400 })
      }

      if (!corners) {
        return NextResponse.json({ error: 'Missing corners' }, { status: 400 })
      }

      // Validate corners format
      if (!corners.topLeft || !corners.topRight || !corners.bottomLeft || !corners.bottomRight) {
        return NextResponse.json(
          {
            error: 'Invalid corners format - need topLeft, topRight, bottomLeft, bottomRight',
          },
          { status: 400 }
        )
      }

      // Ensure venv is ready
      const venvResult = await ensureVenvReady()
      if (!venvResult.success) {
        return NextResponse.json(
          { error: `Python environment not ready: ${venvResult.error}` },
          { status: 500 }
        )
      }

      // Prepare input for Python script
      const pythonInput = JSON.stringify({
        image_base64: imageData,
        corners: [corners.topLeft, corners.topRight, corners.bottomLeft, corners.bottomRight],
        apply_masking: applyMasking,
        apply_augmentation: applyAugmentation,
      })

      // Path to the pipeline preview script
      const scriptPath = path.join(
        process.cwd(),
        'scripts',
        'train-boundary-detector',
        'pipeline_preview.py'
      )

      // Run Python script using the training venv
      const result = await new Promise<PipelinePreviewResult>((resolve, reject) => {
        const pythonProcess = spawn(TRAINING_PYTHON, [scriptPath], {
          cwd: path.join(process.cwd(), 'scripts', 'train-boundary-detector'),
          env: PYTHON_ENV,
        })

        // Send input via stdin
        pythonProcess.stdin.write(pythonInput)
        pythonProcess.stdin.end()

        let stdout = ''
        let stderr = ''

        pythonProcess.stdout.on('data', (data) => {
          stdout += data.toString()
        })

        pythonProcess.stderr.on('data', (data) => {
          stderr += data.toString()
        })

        pythonProcess.on('close', (code) => {
          if (code !== 0) {
            reject(new Error(`Python script failed: ${stderr}`))
            return
          }

          try {
            const output = JSON.parse(stdout)
            resolve(output)
          } catch {
            reject(new Error(`Failed to parse Python output: ${stdout}`))
          }
        })

        pythonProcess.on('error', (err) => {
          reject(err)
        })
      })

      return NextResponse.json({
        success: true,
        pipeline: result,
      })
    } catch (error) {
      console.error('[preview-augmentation] Error:', error)
      return NextResponse.json(
        { error: error instanceof Error ? error.message : 'Unknown error' },
        { status: 500 }
      )
    }
  },
  { role: 'admin' }
)