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