User Tools

Site Tools


notes:circle-detector

Circle Detector

* TODO: Replace cameraToProjector with quad translation implementation from virtual-programs/mask-tags.folk

  • TODO: affix relative codepaths below
  • Copy the C++ code paths below onto your Folk machine (make sure your directory names match the paths below)
  • Print out the Folk programs
  • Try it out:

[C++]: OpenCVWrapper.h
#ifndef OPENCV_WRAPPER_H
#define OPENCV_WRAPPER_H
 
#ifdef __cplusplus
extern "C" {
#endif
 
typedef struct {
    uint32_t width;
    uint32_t height;
    int components;
    uint32_t bytesPerRow;
    uint8_t *data;
} cv_image_t;
 
typedef struct {
 float x;
 float y;
 float radius;
} cv_keypoint_t;
 
uint32_t cvSimpleBlobDetector(cv_image_t img, cv_keypoint_t* kpts, uint32_t count, bool use_hough);
 
#ifdef __cplusplus
}
#endif
 
#endif // OPENCV_WRAPPER_H
[C++]: OpenCVWrapper.cpp
#include <opencv2/opencv.hpp>
#include <iostream>
 
extern "C" {
    #include "OpenCVWrapper.h"
 
    cv::Mat convertToMat(const cv_image_t& img) {
        int type = 0;
 
        // Determine the correct type based on number of components
        if (img.components == 1) {
            type = CV_8UC1; // Grayscale
        } else if (img.components == 3) {
            type = CV_8UC3; // RGB
        } else {
            // Add more cases if needed
            throw std::runtime_error("Unsupported number of components");
        }
 
        // Create cv::Mat with the given size, type, and data
        // Note: The step is the number of bytes per row
        return cv::Mat(img.height, img.width, type, img.data, img.bytesPerRow);
    }
 
    cv_image_t convertToImageT(const cv::Mat& mat) {
        cv_image_t img;
 
        img.width = static_cast<uint32_t>(mat.cols);
        img.height = static_cast<uint32_t>(mat.rows);
        int type = mat.type();
        if (type == CV_8UC1) {
            img.components = 1;
        } else if (type == CV_8UC3) {
            img.components = 3;
        }
        img.bytesPerRow = static_cast<uint32_t>(mat.step); // or mat.step1()
 
        // Allocate memory for the data
        size_t dataSize = mat.step * mat.rows; // or mat.total() * mat.elemSize()
        img.data = new uint8_t[dataSize];
 
        // Copy the data from the cv::Mat
        std::memcpy(img.data, mat.data, dataSize);
 
        return img;
    }  
 
    uint32_t cvSimpleBlobDetector(cv_image_t src, cv_keypoint_t* kpts, uint32_t count, bool use_hough) {
        cv::Mat gray = convertToMat(src);
        cv::Mat blur;
 
        const int maxsize = 760;
        float ratio =  float(maxsize) / src.height;
        if (src.height > maxsize) {
          cv::resize( gray, gray, cv::Size(), ratio, ratio, cv::INTER_NEAREST);
          cv::medianBlur(gray, blur,  3);
        } else {
          cv::medianBlur(gray, blur,  11);
        }
 
        //cv::Mat contour(blur.size(), CV_8UC3, cv::Scalar(0,0,0));
        //cv::cvtColor(blur, contour, cv::COLOR_GRAY2RGB);
 
        int maxCount = 0;
 
        if (use_hough) {
          // Use Hough transform
          std::vector<cv::Vec3f> circles;
          int minRadius = 10;
          int maxRadius = 20;
          int minDist = minRadius;
 
          //cv::HoughCircles(blur, circles, cv::HOUGH_GRADIENT, 1, minDist, param1, param2, minRadius, maxRadius);
          cv::HoughCircles(blur, circles, cv::HOUGH_GRADIENT_ALT, 1, minDist, 300, 0.9, minRadius, maxRadius);
          maxCount = std::min<uint32_t>(circles.size(), count);
 
          // extract the x y coordinates of the keypoints:
          for (int i=0; i<maxCount; i++){
            cv::Vec3f& c = circles[i];
            kpts[i].x = round(c[0] / ratio);
            kpts[i].y = round(c[1] / ratio);
            kpts[i].radius = round(c[2] / ratio);
            //cv::circle(contour, cv::Point(c[0], c[1]), c[2], cv::Scalar(0, 0, 255), 1.0, cv::LINE_AA);
          }
 
        } else {
          // Storage for blobs
          std::vector<cv::KeyPoint> keypoints;
 
          // Setup SimpleBlobDetector parameters.
          cv::SimpleBlobDetector::Params params;
 
          // Change thresholds
          params.minRepeatability = 2;
          //params.thresholdStep = 150;
          //params.maxThreshold = 255;
          params.filterByArea = true;
          //params.minArea = 3000;
          //params.maxArea = 8000;
          //params.minArea = 3000 / (float(maxsize) / src.height);
          //params.maxArea = 8000 / (float(maxsize) / src.height);
          params.filterByCircularity = true;
          params.minCircularity = 0.87;
 
          cv::Ptr<cv::SimpleBlobDetector> detector = cv::SimpleBlobDetector::create(params);
          detector->detect(blur, keypoints);
 
          maxCount = std::min<uint32_t>(keypoints.size(), count);
 
          // extract the x y coordinates of the keypoints:
          for (int i=0; i<maxCount; i++){
            kpts[i].x = keypoints[i].pt.x;
            kpts[i].y = keypoints[i].pt.y;
            kpts[i].radius = keypoints[i].size / 2;
            //cv::circle(contour, keypoints[i].pt, keypoints[i].size/2, cv::Scalar(255, 0, 0), 2, cv::LINE_AA);
          }
 
        }
        //cv::imwrite("/tmp/opencv.jpg", contour);
        return maxCount;
    }
 
}
[Folk] dots.folk
Wish $this is titled "Dot detector"

# Dot detector using OpenCV
 
set makeDotDetector {{} {
    set detector DotDetector
    namespace eval $detector {
 
        set cc [c create]
 
        $cc cflags -I$::env(HOME)/opencv
        $cc include <stdio.h>
        $cc include <stdlib.h>
        $cc include <assert.h>
        $cc include "$::env(HOME)/ocv4/OpenCVWrapper.h"
 
        ::defineImageType $cc
        $cc import ::Heap::cc folkHeapAlloc as folkHeapAlloc
        $cc import ::Heap::cc folkHeapFree as folkHeapFree
 
        $cc proc detect {image_t gray} Tcl_Obj* {
            assert(gray.components == 1);
            cv_image_t im = (cv_image_t) { .width = gray.width, .height = gray.height, .components = gray.components, .bytesPerRow = gray.bytesPerRow, .data = gray.data };
 
            int max_count = 100;
            cv_keypoint_t* detections = (cv_keypoint_t*)malloc(sizeof(cv_keypoint_t)*max_count);
 
            int detectionCount = cvSimpleBlobDetector(im, detections, max_count, true);
 
            Tcl_Obj* detectionObjs[detectionCount];
            for (int i = 0; i < detectionCount; i++) {
                cv_keypoint_t* det = &detections[i];
 
                detectionObjs[i] = Tcl_ObjPrintf("center {%f %f} radius %f",
                                                 det->x, det->y, det->radius);
            }
            free(detections);
            Tcl_Obj* result = Tcl_NewListObj(detectionCount, detectionObjs);
            return result;
        }
 
        c loadlib $::env(HOME)/ocv4/libopencvwrapper.so
        $cc compile
 
        namespace export *
        namespace ensemble create
 
    }
 
    return $detector
}}

# Plain detector. Runs on entire camera frame.
set detectorProcess [Start process {
 
  set detector [apply $makeDotDetector]
 
  Wish $::thisProcess receives statements like \
      [list /someone/ claims camera /cam/ has frame /grayFrame/ at timestamp /timestamp/]
  Wish $::thisProcess shares statements like \
      [list /someone/ claims /process/ detects dots /dots/ at /timestamp/ in time /dotTime/]
 
  When camera /cam/ has frame /grayFrame/ at timestamp /timestamp/  {
      set dotTime [time {
          set dots [$detector detect $grayFrame]
      }]
      Claim $::thisProcess detects dots $dots at $timestamp in time $dotTime
  }
}]
 
 
When /someone/ detects dots /dots/ at /timestamp/ in time /dotTime/ & \
   /obj/ has region /region/ {
 
  set dots [lmap dot $dots { dict create center [lmap d [::cameraToProjector [dict get $dot center]] {round $d }] }]
 
  set contained [list ]
  foreach dot $dots {
     if {[region contains $region [dict get $dot center]]} {
        lappend contained $dot
     }
  }
  if {[llength $contained] > 0} {
    Claim $obj contains dots $contained
  }
}
[Folk] slider.folk
Commit { Claim $this has value 0 }
 
Wish $this is outlined white
 
When $this has region /r/ {
 
  set width [region width $r]
  set height [region height $r]
 
  if {$width > $height} {
    set maxdim $width
    set base "left"
    set dir "left"
  } else {
    set maxdim $height
    set base "bottom"
    set dir "down"
  }
 
  set origin [region $base $r]
 
  When $this contains dots /dots/ {
    if {[llength $dots] == 1} {
 
      set angle [* -1 [region angle $r]]
      # Calculate position of dot in region space
      set dot [dict get [lindex $dots 0] center]
      set rel [::vec2 rotate [::vec2 sub $dot $origin] $angle]
 
      set idx [expr {$width > $height ? 0 : 1 }]
      # Calculate the fraction of the width indicated by the dot
      set frac [expr {abs([lindex $rel $idx]) / $maxdim}]
 
      Commit { Claim $this has value $frac }
    }
  }
 
  When $this has value /v/ {
 
    set shift [expr {(1 - $v) * ($maxdim / 2)}]
    set scale [expr {$v * 100}]
    set dim [expr {$width > $height ? "width" : "height"}]
    set slider [region move [region scale $r $dim ${scale}%] $dir ${shift}px]
 
    Wish to draw a polygon with points [region vertices $slider] color rgba(255,255,255,0.1)
 
    Wish $this is labelled [expr {round($v)}]
  }
}
notes/circle-detector.txt · Last modified: 2024/10/21 23:01 by admin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki