OpenCV Object Detection (HOGDescriptor) on iOS - c++

I'm trying to get the people detector provided by the OpenCV library running. So far I get decent performance on my iPhone 6 but the detection is super bad and almost never correct and I'm not really sure why this is since you can find example videos using the same default HOG descriptor with way better detection.
Here is the code:
- (void)processImage:(Mat&)image {
cv::Mat cvImg, result;
cvtColor(image, cvImg, COLOR_BGR2HSV);
cv::vector<cv::Rect> found, found_filtered;
hog.detectMultiScale(cvImg, found, 0, cv::Size(4,4), cv::Size(8,8), 1.5, 0);
size_t i;
for (i=0; i < found.size(); i++) {
cv::Rect r = found[i];
rectangle(image, r.tl(), r.br(), Scalar(0,255,0), 2);
}
}
The video input comes from the iPhone camera itself and "processImage:" is called for every frame. For the HOGDescriptor I use the default people detector:
_hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());
I appreciate any help. :)

I'm new to openCV, so take this with a grain of salt:
The line cvtColor(image, cvImg, COLOR_BGR2HSV); converts the image from the BGR color space to the HSV color space. Essentially, it changes each pixel from being represented by how much blue, green, and red it has, to being represented by the components hue (color), saturation (how much color) and value (how bright). Clearly, the hogDescriptor acts on a BGR image, not an HSV image. You need to pass it a type CV_8UC3 image: An image with 3 channels per pixel (C3), ex. BGR, and an 8bit unsigned number for each channel (8U), This part is less important. What are you passing into the method processImage()? It should be one of those types. If not, you need to know the type and convert it to CV_8UC3 using the cvtColor() method

Related

How to increase the saturation values of an image using HSV (in OpenCV using C++)?

I was looking for a way to increase the saturation of some of my images using code and found the strategy of splitting a material with HSV and then increasing the S channel by a factor. However, I ran into some issues where the split channels were still in BGR (I think) because the output was just a greener tinted version of the original.
//Save original image to material
Mat orgImg = imread("sunset.jpg");
//Resize the image to be smaller
resize(orgImg, orgImg, Size(500, 500));
//Display the original image for comparison
imshow("Original Image", orgImg);
Mat g = Mat::zeros(Size(orgImg.cols, orgImg.rows), CV_8UC1);
Mat convertedHSV;
orgImg.convertTo(convertedHSV, COLOR_BGR2HSV);
Mat saturatedImg;
Mat HSVChannels[3];
split(convertedHSV, HSVChannels);
imshow("H", HSVChannels[0]);
imshow("S", HSVChannels[1]);
imshow("V", HSVChannels[2]);
HSVChannels[1] *= saturation;
merge(HSVChannels, 3, saturatedImg);
//Saturate the original image and save it to a new material.
//Display the new, saturated image.
imshow("Saturated", saturatedImg);
waitKey(0);
return 0;
This is my code and nothing I do makes it actually edit the saturation, all the outputs are just green tinted photos.
Note saturation is a public double that is usually set to around 1.5 or whatever you want.
Do not use cv::convertTo() here. It changes the bitdepth (and representation, int vs. float) of the image, not what you are trying to achieve, the color space.
Using it like that does not throw a warning or error though, because both type indicators (CV_8U, ...) and the colorspace indicators (COLOR_BGR2HSV,...) can be resolved as integers, one is a #define, the other a old style enum.
Following the example here, it is possible to do with cv::cvtColor(). Don't forget to revert back before showing the image, imshow() and imwrite() both expect an BGR format.
// Convert image from BGR -> HSV:
// orgImg.convertTo(convertedHSV, COLOR_BGR2HSV); // <- this wrong, do not use
cvtColor(orgImg, convertedHSV, COLOR_BGR2HSV); // <- this does the trick instead
// to the split, multiplication, merge
// [...]
// Convert image back HSV -> BGR:
cvtColor(saturatedImg, saturatedImg, COLOR_HSV2BGR);
//Display the new, saturated image.
imshow("Saturated", saturatedImg);
Note that oCV does not care about color representation when working with a 3 channel Mat: Could be RGB, HSV or anything else. Only for displaying (or saving to an image format) does the given color space matter.

Real-time Image Processing: Noise in HSV image (openCV)

I am doing a real-time shapes and colors classification system with very high accuracy. It seems like my preprocessing phase is not good enough so that the result is not as accurate as I expected. Here is what I'm doing:
Take data from the Camera can crop it to receive ROI.
Convert ROI Image from RGB to HSV space.
Using a median filter to reduce noise in HSV image.
Threshold the image
Using dilate and erode to remove small holes and small objects in Image
Using findContours and approxPolyDP to detect square objects.
This is my preprocessing phase:
image_cv = cv::cvarrToMat(image_camera);
Mat cropped = image_cv(cv::Rect(0, 190, 640, 110));
imshow("origin", cropped);
Mat croppedCon = CropConveyor(cropped);
cv::cvtColor(croppedCon, croppedCon, CV_RGB2HSV);
medianBlur(croppedCon, croppedCon, 3);
cv::Mat binRect;
cv::inRange(croppedCon, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), binRect);
This is the code for detecting squares:
vector<vector<Point>> contours;
findContours(binarizedIm, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
vector<Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
//double arclength = arcLength(Mat(contours[i]), true);
approxPolyDP(Mat(contours[i]), approx, 3.245 , true); //0.04 for wood
if (approx.size() != 4) continue;
if (isContourConvex(Mat(approx)) && contourArea(Mat(approx)) > 250)
{
double MaxCos = 0;
for (int j = 2; j < 5; j++)
{
double cos = angle(approx[j % 4], approx[j - 1], approx[j - 2]);
MaxCos = MAX(cos, MaxCos);
}
if (MaxCos < 0.2)
squares.push_back(approx);
}
}
I think noise in HSV Image is the main reason. Here is some images illustrating my problems. I saw a lot of noise in HSV Image, that's why I use a media filter to it to reduce noise but preserve the edges becase I think that edges information is very important when using findContours function.
HSV and HSV in separate channels
My question is:
What is the noise in HSV Image, refer to the above Image, how can I
enhance my Image's quality?
The reason for noise in your saturation image is noise in your input image. Caused by a bad camera / optics and further increased by JPEG compression.
That's by far the worst image I have seen in years. You shouldn't invest another second into processing that, unless you live on Mars and need results tomorrow.
Your input image is super noisy, undersampled, defocussed, underexposed, full of aliasing and compression artifacts and pretty much anything else you can do wrong with an image.
First rule of signal processing:
crap in = crap out
You can get much better cameras basically for free. Find and use one.
Part of the problem is that you're doing the noise reduction in HSV space. In your example you can see the V channel is better-behaved than H and S. It would be better to do noise-reduction in RGB (which is more linear and closer, though not identical, to the camera's native colour space where the noise originates; of course there's also gamma-correction).
Maybe consider a stronger edge-preserving noise-reducing filter such as Bilateral Filter.
I don't get it why are you using HSV for segmenting the objects, the RGB image is good enough. Separate the image into 3 channels (r,g,b) and apply an adaptive threshold on them. dilate and erode the images then add (not merging) those 3 binary images to have one binary image. Finally do level 6 of your recipe to extract the objects. If the noise still effects the result, apply a bilateral filter on r,g,b channels before the threshold.

convert bgr to hsv in opencv, C++

I'm trying to have a webcam take a picture of someone's face in BGR, convert the picture into HSV, and analyze these HSV values that will later be used in a skin detection algorithm. Unfortunately, the picture seems to be analyzed in BGR, even after I try to convert it using cvtColor().
I use the code below to test whether or not I'm using the right color space. Note the part where I try to set saturation and value to 0:
Mat faceROI = findFace(first); //basic Mat, region of interest for face (code not included)
Mat temp;
faceROI.convertTo(temp, CV_8UC3); //making sure this has right no. of channels and such
CvScalar s;
IplImage face_ipl = temp; //new header
IplImage* aNew = cvCreateImage(cvGetSize(&face_ipl), face_ipl.depth, 3);
cvCvtColor(&face_ipl, aNew, CV_BGR2HSV);
for(int x = 0; x < faceROI.cols; x++){
for (int y = 0; y < faceROI.rows; y++){
s = cvGet2D(aNew, x, y);
//vvvvvvvvvvv
s.val[1] = 0; //should be saturation
s.val[2] = 0; //should be value
//^^^^^^^^^^^
cvSet2D(aNew, x, y, s);
}
}
Mat again(aNew); //<--- is this where something is set back to BGR?
imshow("white", again);
cvReleaseImage(&aNew);
This produces a completely blue picture of my face, so it seems likes I'm editing the G and R channels of a BGR image, instead of the S and V channels of an HSV image. (I'd post the image, but this is my first post so I don't have enough reputation yet.)
Does anybody know why this is happening? Any and all thoughts are appreciated.
You're mixing up the C++ Mat style with the old C IplImage*, this makes it confusing to see what exactly is going on. Here is the code to turn inputImage into HSV:
Mat fullImageHSV;
cvtColor(inputImage, fullImageHSV, CV_BGR2HSV);
Be aware that the OpenCV HSV values are H from 0-180 while S and V are from 0-255 while other programs tend to use different values. ALso note that OpenCV is unable to show HSV images normally, this distorts the color because they are being interpreted as RGB.

Depth feed from Kinect not updating

I'm using a combination of OpenKinect and OpenCV libraries to apply Haar-like feature recognition to both RGB and depth images.
I can get the live feed and successfully detect objects using the RGB feed however the depth is giving me massive problems.
After the initial frame the depth frame does not seem to update at all.
The depth callback function that provides the raw data is as follows:
//depth callback function
void depth_cb(freenect_device *dev, void *v_depth, uint32_t timestamp)
{
if (got_depth == 0){
pthread_mutex_lock(&buf_mutex);
//copy to OpenCV buffer
memcpy(depthMat.data, v_depth, (640*480*2));
// depthMat.convertTo(depthFrame, CV_8UC1, 256.0/2048.0);
got_depth++;
pthread_cond_signal(&frame_cond);
pthread_mutex_unlock(&buf_mutex);
}
}
the Mats used are initialised like so:
cv::Mat depthMat(cv::Size(640,480),CV_16UC1);
cv::Mat depthFrame(cv::Size(640,480),CV_8UC1);
And in the main function I try use them like so:
depthMat.convertTo(depthFrame, CV_8UC1, 255.0/2048.0);
imshow("rgb", rgbMat);
imshow("depth-pre-conversion", depthMat);
imshow("depth", depthFrame);
IplImage depthImage = depthFrame;
IplImage rgbImage = rgbMat;
detect_and_draw(&depthImage);
'Depth-pre-conversion' is a almost black frame, you can just about make out the depth image here. It doesn't update.
'Depth' is the lighter version once converted to 8 bits, it also doesn't move.
'rgb' is the live RGB feed which works no problem (although it is BGR rather than RGB but I'll get round fixing that at some point, it's less important right now)
I'd appreciate any advise and help you can offer.

Canny edge detection - grayscale images always coming up as 3-channel, unusable?

I am working through the book "Learning OpenCV" from the O'Reilly series and am trying to perform a canny edge detection sample.
Any grayscale image I choose seems to come up as having 3 channels, and to the best of my knowledge, canny only works with single channel images, so this always fails. I am even using the images provided by OpenCV.
Here is my code..
IplImage* doCanny(IplImage* in, double lowThresh, double highThresh, double aperture)
{
if(in->nChannels != 1)
return(0); //canny only handles gray scale images
IplImage* out = cvCreateImage(cvSize(in->width, in->height), IPL_DEPTH_8U, 1);
cvCanny(in, out, lowThresh, highThresh, aperture);
return(out);
};
IplImage* img = cvLoadImage("someGrayscaleImage.jpg");
IplImage* out = doCanny(img, 10, 100, 3);
Why might this always give me 3-channel images? How can I solve this?
You can use this method with another parameter
IplImage* cvLoadImage(const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR)
#define CV_LOAD_IMAGE_COLOR 1
#define CV_LOAD_IMAGE_GRAYSCALE 0
#define CV_LOAD_IMAGE_UNCHANGED -1
The default parameter is load image with color. What you have to do is to load it with grayscale
Here is an example
cvLoadImage("yourimage.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Here is the detail explanation for that method. You can look at here for more details:
Open CV 2.0 References
scolor – Specific color type of the loaded image: if $ > 0 $, the loaded image is forced to be a 3-channel color image; if 0, the loaded image is forced to be grayscale; if $ < 0 $, the loaded image will be loaded as is (note that in the current implementation the alpha channel, if any, is stripped from the output image, e.g. 4-channel RGBA image will be loaded as RGB).