//Original Author: Jacob Haip.
Contributions welcome!//
Instructions for integrating OpenCV with Folk. This guide involves a manual compilation set outside of folk, but the creation of the wrapper files and the compilation (via an TCL "exec" call) could also be moved within a folk program to completely encapsulate the code.
1. Install OpenCV
sudo apt-get install -y libopencv-dev
2. Create a C wrapper for the OpenCV functions you want to use. I saved these files in a new ~/folk/play/opencv folder.
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;
int getWidthOfImage(cv_image_t img);
cv_image_t myAdaptiveThreshold(cv_image_t img);
#ifdef __cplusplus
}
#endif
#endif // OPENCV_WRAPPER_H
OpenCVWrapper.cpp
#include
#include
extern "C" {
#include "OpenCVWrapper.h"
int getWidthOfImage(cv_image_t img) {
return img.width;
}
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(mat.cols);
img.height = static_cast(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(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;
}
cv_image_t myAdaptiveThreshold(cv_image_t img) {
cv::Mat inputImage = convertToMat(img);
cv::Mat outputImage;
adaptiveThreshold(inputImage, outputImage, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 11, 2);
return convertToImageT(outputImage);
}
}
3. Compile the wrapper
g++ -fPIC -shared OpenCVWrapper.cpp -o libopencvwrapper.so `pkg-config --cflags --libs opencv4`
4. Use the compiled libopencvwrapper.so and .h file in your folk C code
set cc [c create]
defineImageType $cc
$cc cflags -I$::env(HOME)/folk/play/opencv
$cc include
$cc include "OpenCVWrapper.h"
if {[namespace exists ::Heap]} {
$cc import ::Heap::cc folkHeapAlloc as folkHeapAlloc
$cc import ::Heap::cc folkHeapFree as folkHeapFree
} else {
$cc code { #define folkHeapAlloc malloc }
$cc code { #define folkHeapFree free }
}
$cc proc opencvAdaptiveThreshold {image_t img} image_t {
cv_image_t im2 = (cv_image_t) { .width = img.width, .height = img.height, .components = img.components, .bytesPerRow = img.bytesPerRow, .data = img.data };
cv_image_t r = myAdaptiveThreshold(im2);
image_t ret = (image_t) { .width = r.width, .height = r.height, .components = r.components, .bytesPerRow = r.bytesPerRow, .data = folkHeapAlloc(r.bytesPerRow * r.height) };
memcpy(ret.data, r.data, r.bytesPerRow * r.height);
return ret;
}
c loadlib $::env(HOME)/folk/play/opencv/libopencvwrapper.so
$cc compile
When $this has region /r/ {
Claim $this' has region [region move $r down 110%]
}
set ::opencvOldFrames [list]
When $this' has camera slice /slice/ & $this has region /r/ {
set grayImg [opencvAdaptiveThreshold $slice]
set center [region centroid $r]
Wish to draw an image with center $center image $grayImg radians 0 scale 2
lappend ::opencvOldFrames $grayImg
if {[llength $::opencvOldFrames] >= 10} {
set ::opencvOldFrames [lassign $::opencvOldFrames oldestFrame]
image freeImage $oldestFrame
}
}
{{:guides:opencv-threshold-example.jpeg?400|}}