I’m currently trying to manually threshold an image so that I can threshold a HSV image based on color of objects. To begin I’ve got a simple threshold like so:
Which should threshold every pixel above brightness 100 to white and everything below it to black, which I then run a contour finder on.
The problem I have is that the thresholded value put the the console is either 255 or 0 as expected, but the image when drawn to the screen is pure black and the contour finder finds nothing.
Is there something I’m missing with the ofxOpenCv implementation?
void ofApp::setup(){
// initialize image properties
imgWidth = 320;
imgHeight = 240;
// initialize object detection properties
detectedObjectMax = 10; // maximum of 10 detected object at a time
contourMinArea = 40; // detect a wide range of different sized objects
contourMaxArea = (imgWidth * imgHeight) / 3;
// initialize OpenCV image instances
// (manual memory allocation required)
originalInputImg.allocate(imgWidth, imgHeight);
hsvImg.allocate(imgWidth, imgHeight);
hueImg.allocate(imgWidth, imgHeight);
saturationImg.allocate(imgWidth, imgHeight);
valueImg.allocate(imgWidth, imgHeight);
backgroundImg.allocate(imgWidth, imgHeight);
bckgrndHueDiffImg.allocate(imgWidth, imgHeight);
// initialize camera instance
debugCameraDevices(); // print information about available camera sources
cameraInput.setDeviceID(0);
// initialize helper values
labelPosDelta = 14;
blobOverlayRadius = 10;
}
//--------------------------------------------------------------
void ofApp::update(){
// update (read) input from camera feed
cameraInput.update();
// check if a new frame from the camera source was received
if (cameraInput.isFrameNew()){
// read (new) pixels from camera input and write them to original input image instance
originalInputImg.setFromPixels(cameraInput.getPixels());
inputImg = originalInputImg;
// Blur inputImg to counteract for noise from input
inputImg.blurGaussian(5);
// create HSV color space image based on original (RGB) received camera input image
hsvImg = inputImg;
hsvImg.convertRgbToHsv();
// extract HSV color space channels into separate image instances
hsvImg.convertToGrayscalePlanarImages(hueImg, saturationImg, valueImg);
// For every pixel in img
for (int i=0; i<(hsvImg.height+hsvImg.width)*3; i+=3){
// Get current pixel value
int p = hsvImg.getPixels()[i];
// Shift pixel value by 200 so red is closest to max in hue
p += 220;
p %= 255;
hsvImg.getPixels()[i] = p;
}
// :TODO: threshold image based on redness, currently doesn't work
for (int i=0; i<(hsvImg.height+hsvImg.width)*3; i+=3){
if (hsvImg.getPixels()[i+2]>100){
bckgrndHueDiffImg.getPixels()[i/3] = 255;
} else {
bckgrndHueDiffImg.getPixels()[i/3] = 0;
}
}
// apply object detection via OpenCV contour finder class
contourFinder.findContours(bckgrndHueDiffImg, contourMinArea, contourMaxArea, detectedObjectMax, false);
}
}
void ofApp::draw(){
// reset color for drawing
ofSetHexColor(0xffffffff); // set color "white" in hexadecimal representation
// draw grid of images
//
// row 1
originalInputImg.draw(0 * imgWidth, 0 * imgHeight); // draw original input image as received from camera source
hsvImg.draw(1 * imgWidth, 0 * imgHeight); // original input image in HSV color space representation
// row 2
hueImg.draw(0 * imgWidth, 1 * imgHeight);
saturationImg.draw(1 * imgWidth, 1 * imgHeight);
valueImg.draw(2 * imgWidth, 1 * imgHeight);
// row 3
saturationImg.draw(0 * imgWidth, 2 * imgHeight); // copy of saturation image in order to put colored circles on detection objects in the scene
bckgrndHueDiffImg.draw(1 * imgWidth, 2 * imgHeight);
// visualize object detection
// draw detected objects ("blobs") individually
for (int i = 0; i < contourFinder.nBlobs; i++) {
// access current blob
contourFinder.blobs[i].draw(2 * imgWidth, 2 * imgHeight); // draw current blob in bottom right image grid
// extract RGB color from the center of the current blob based on original input image
//
// get pixel reference of original input image
//ofPixels originalInputImagePxls = originalInputImg.getPixelsRef(); // OF version 0.8.4
ofPixels originalInputImagePxls = originalInputImg.getPixels(); // OF version 0.9.0
// get point reference to the center of the current detected blob
ofPoint blobCenterPnt = contourFinder.blobs[i].centroid;
// get color of pixel in the center of the detected blob
//ofColor detectedBlobClr = originalInputImagePxls.getColor(blobCenterPnt.x, blobCenterPnt.y); // OF version 0.9.0
ofColor detectedBlobClr = originalInputImagePxls.getColor(blobCenterPnt.x / 3, blobCenterPnt.y / 3); // OF version 0.9.6
// apply detected color for drawing circle overlay
ofSetColor(detectedBlobClr);
ofFill();
// draw circle overlay in bottom left image of the grid (ontop of a copy of the saturation image)
// OF version 0.8.4
/*ofCircle(blobCenterPnt.x + 0 * imgWidth,
blobCenterPnt.y + 2 * imgHeight,
blobOverlayRadius); */
// OF version 0.9.0
ofDrawCircle(blobCenterPnt.x + 0 * imgWidth,
blobCenterPnt.y + 2 * imgHeight,
blobOverlayRadius);
}
}
Excuse the mess, I’ve been transistioning from one detection method to another and was waiting to get it functioning before tidying up
And for good measure, here is the OfApp.h file:
#pragma once
#include "ofMain.h"
#include "ofxOpenCv.h" // make functionalities of OpenCV addon available
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
// image properties
int imgWidth;
int imgHeight;
// object detection properties
int detectedObjectMax; // representing the maximum amount of detected objects
int contourMinArea; // presenting the minimum amount of adjacent pixels in order to detect an object
int contourMaxArea; // presenting the maximum amount of adjacent pixels in order to detect an object
// image instances (managed by OpenCV)
ofxCvColorImage originalInputImg; // original image as received from camera source in RGB color space
ofxCvColorImage inputImg; // original image duplicate, used for processing for later stages
ofxCvColorImage hsvImg; // representing the original input image in HSV color space
ofxCvGrayscaleImage hueImg; // representing the hue channel of the HSV image
ofxCvGrayscaleImage saturationImg; // representing the saturation channel of the HSV image
ofxCvGrayscaleImage valueImg; // representing the value channel of the HSV image
ofxCvGrayscaleImage backgroundImg; // registred background image in order to assist object detection
ofxCvGrayscaleImage bckgrndHueDiffImg; // image instance representing the difference between the registered background image and the current saturation color channel image in order to run the object (contour) detection on
// OpenCV contour finder instance for handling object detection
ofxCvContourFinder contourFinder;
// camera instance
ofVideoGrabber cameraInput;
// helper values
int labelPosDelta;
int blobOverlayRadius;
};
You are right. I thought your were using also a color image there…
Try to apply this manual thresholding directly to a color image in the HSV color space -> then convert it into RGB format to -> finally convert it into grayscale to apply it into FindContours.
For anyone else in a similar problem, I think the issue I had was that I needed to call imag.flagImageChanged() once I was done changing the pixel values so that OpenCV knew to update the shader.
So just a quick pseudo-code example would look like this:
for (i 0 to num_pixels -1){
image.getpixel[i] = 255
}
image.flagImageChanged()
I’ve still got an issue whereby only the top few rows of the image are updating but that may just be a case of me not setting every pixel/doing something else silly.
Hey Servin,
Good point, your image was not really updating after your manual pixel updating.
Check your loop acces to pixels for (int i = 0; i<(hsvImg.height + hsvImg.width) * 3; i += 3) {
might be like this for (int i = 0; i<(hsvImg.height * hsvImg.width) * 3; i += 3) {
Hope now this helps
I share here my short example ( equal than OpencvExample with that HSV threshold). If you or somebody else want to check it, there I’ve required Rgb color space back. Color conversion to grayscale is taking care of RGB mode only.