Skip to menu

Robotics with Object Pascal

Rover

Depth_test.png

 

program LiveMiDasDepthEstimation;

{$MODE Delphi} {$H+}

uses
  SysUtils, StdCtrls,
  Messages, Variants, Classes, Graphics,
  Controls, Dialogs, Interfaces,
  OPENCVWrapper; // Your installed OpenCV Wrapper unit

const
  // MiDaS model file path (Adjust this path as needed)
  MODEL_FILE_NAME = '../../Midas-v2.1-model-small.onnx';

  // Window names
  WIN_ORIGINAL = 'Original Feed';
  WIN_DEPTH = 'MiDaS Depth Map';

// Define global variables for OpenCV objects
var
  W_IN: integer =  256;
  H_IN: Integer =  256;
  SCALE: Single  = 0.003922;

  // Camera capture handle
  capture: PCvVideoCapture_t;
  // DNN network handle
  net: PCvdnn_Net_t;

  // Image Mat structures
  frame: PCvMat_t;     // Stores the raw camera frame (BGR)
  outmat: PCvMat_t;    // Stores the raw output blob from the DNN
  heatMap: PCvMat_t;   // Final processed depth heatmap
  outgray: PCvMat_t;   // Grayscale version of the depth map
  outHeatmap: PCvMat_t;
  outColor: PCvMat_t;  // Blended output image

  // Timing and utility variables
  StartCount, StopCount, Freq: Int64;
  TimingSeconds: Double;
  vMin, vMax: Double;
  matdims: array[0..1] of Integer;
  outcvSize: PCvSize_t;
  cvstr: PCVString_t;
  c: Integer; // Key press value
  modelBin: CvString_t;

  winameOrig, winnameDepth : CvString_t;

// --- DNN Initialization ---
procedure InitializeMiDasNet;
begin
  Writeln('Initializing MiDaS DNN...');

  // 1. Read the network model from the ONNX file
  modelBin.pstr := PAnsiChar(MODEL_FILE_NAME);
  net := pCvdnn_readNetFromONNX(@modelBin);

  if pCvdnn_NetEmpty(net) then
  begin
    Writeln('ERROR: Cannot load MiDaS model. Check file path: ', MODEL_FILE_NAME);
    Halt(1);
  end;

  // 2. Set backend and target (use the default for simplicity, typically CPU)
  pCvdnn_NetSetPreferableBackend(net, Ord(DNN_BACKEND_OPENCV));
  pCvdnn_NetSetPreferableTarget(net, Ord(DNN_TARGET_CPU));

  Writeln('MiDaS Network loaded successfully.');
end;

// --- CORE FRAME PROCESSING FUNCTION ---
// This takes a raw frame and generates a depth image
procedure GenerateDepthMap(const InputFrame: Pointer);
var
  blob: PCvMat_t;
  outptr: UInt64;
begin
  // 1. Create Input Blob
  // The MiDaS model requires a specific input size (384x384 or 256x256) and normalization.
  // The DNN code snippet suggests 256x256 was used.
  blob := pCvdnn_BlobFromImage(InputFrame, 1.0/255.0,
    CvSize_(256, 256), // Input size for MiDaS-small
    CvScalar_(123.675, 116.28, 103.53, 0), // Mean values for normalization
    TRUE, // swapRB (MiDaS uses RGB, OpenCV reads BGR)
    FALSE
  );

  // 2. Set the input and run the forward pass
  pCvdnn_NetSetInput(net, blob);

  // Start timing for performance check
  // QueryPerformanceCounter(StartCount); // If using timing

  outmat := pCvdnn_Netforward(net); // Get the raw depth output

  // QueryPerformanceCounter(StopCount); // If using timing
  // ... calculate and display TimingSeconds ...

  // 3. Process the Output (Copied from frmMidasDNN.pas)
  // The output 'outmat' is a 256x256 single-channel float map.
  outptr := pCvMatGetDimPtr(outmat, 0, 0);
  matdims[0] := 256;
  matdims[1] := 256;

  // Map the raw data into a Mat structure
  heatMap := pCvMatCreate(2, @matdims[0],  CV_32F, outptr);

  // 4. Normalize and Convert to Grayscale
  pCvminMaxLoc(heatMap, @vMin, @vMax );
  outgray := pCvMatCreateEmpty;
  // Scale the float depth values (vMin to vMax) to 8-bit image range (0-255)
  pCvMatConvertTo(heatMap, outgray, CV_8UC1, (255.0/(vMax - vMin)));

  // 5. Apply Colormap for Visualization
  outColor := pCvMatCreateEmpty;
  pCvapplyColorMap(outgray, outColor, Ord(COLORMAP_JET));

  // 6. Resize the depth map to match the original frame size for display
  outcvSize := CvSize_(pCvMatGetWidth(InputFrame), pCvMatGetHeight(InputFrame));
  pCvresize(outColor, outColor, outcvSize);

  // 7. Clean up intermediate blob
  pCvMatDelete(blob);
end;

// --- Main Execution ---
begin
  // A. Initialization
  winameOrig.pstr:= PAnsiChar(AnsiString(WIN_ORIGINAL));
  winnameDepth.pstr:= PAnsiChar(AnsiString(WIN_DEPTH));

  //pCvNamedWindow(@WIN_ORIGINAL, Ord(WINDOW_AUTOSIZE));
  //pCvNamedWindow(@WIN_DEPTH, Ord(WINDOW_AUTOSIZE));

  // Initialize the DNN model
  InitializeMiDasNet;

  // 1. Open the camera stream (usually 0 for the default USB camera)
  capture := pCvVideoCaptureCreate;
  pCvVideoCaptureopenV3(capture, 0, Ord(CAP_ANY));

  if not(pCvVideoCaptureisOpened(capture)) then
  begin
    Writeln('ERROR: Cannot open camera stream. Check camera ID or drivers.');
    Halt(1);
  end;

  frame := pCvMatCreateEmpty; // Initialize Mat for storing the frame

  // B. Main Loop
  Writeln('Running... Press ESC or Q to exit.');
  while True do
  begin
    // 2. Read Frame from Stream (The Video Capture part)
    if not(pCvVideoCaptureread(capture, frame)) then Break;

    {if pCvMatIsEmpty(frame) then
    begin
        // If the frame is empty, wait briefly before trying again
        pCvwaitKey(10);
        Continue;
    end;  }

    // 3. Process Frame for Depth
    GenerateDepthMap(frame); // Uses the frame to generate outColor (the depth image)

    // 4. Display Results
    pCvimshow(@winameOrig, frame);
    pCvimshow(@winnameDepth, outColor);

    // 5. Handle Exit (Press ESC or 'q')
    c := pCvwaitKey(1); // Wait for 1 millisecond
    if (c = 27) or (c = Ord('q')) then Break;

    // 6. Clean up Mats used in the loop
    // Note: frame is reused, but outColor and outmat need to be released
    // if they were created inside the loop and not reused.
    // Since pCvMatCreateEmpty is used inside GenerateDepthMap, the old pointer must be released.
    // In this simplified example, we rely on the library's internal handling
    // or manual release at the end, but for safety in large loops,
    // ensure all temporary Mats are released after use.
  end;

  // C. Cleanup
  Writeln('Cleaning up and exiting.');
  pCvVideoCaptureRelease(capture);
  pCvdnn_NetDelete(net);
  pCvMatDelete(frame);
  // Release any other dynamically allocated Mat objects (outColor, outgray, etc.)

end.