iOS: OpenCV Red color range - ios

I am trying to encircle Red color in the picture using OpenCV library. I have seen many question in google but no one helped for me. My code is below:
void highLightRed(const cv::Mat& inputFrame, cv::Mat& outputFrame)
{
cv::Mat gray, edges, red, blurred;
// blur will enhance edge detection
medianBlur(inputFrame, blurred, 9);
// give image in gray color space
getGray(blurred, gray);
cv::inRange(gray, cv::Scalar(0,0,112), cv::Scalar(60,0,225), red); //BGR
// using Canny algorithm to find edges
cv::Canny(red, edges, 50, 150);
std::vector< std::vector<cv::Point> > c;
// find contours of edges
cv::findContours(edges, c, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
NSLog(#"size: %lu", c.size());
inputFrame.copyTo(outputFrame);
// draw contour in green (0,200,0) with thinkness 3
//cv::drawContours(outputFrame, c, -1, cv::Scalar(0,200,0),3);
cv::drawContours(outputFrame, c, -1, cv::Scalar(0,200,0),2);
}
void getGray(const cv::Mat& input, cv::Mat& gray)
{
const int numChannes = input.channels();
if (numChannes == 4)
{
cv::cvtColor(input, gray, cv::COLOR_BGRA2GRAY);
}
}
Problem: This code works perfect if only Red and white color are there in picture, it encircles red color successfully. But if multiple colors specially dark colors are involved the color detection fails.
I need exact range which only detect Red color. Thanks

Finding the exact range of color in BGR or RGB format is very difficult. You will have to play around it a lot (if you are a newbie).
A very simple way:
Convert your image into HSV format using cvtColor()
Split your HSV image into three channel i.e. H, S and V-channel.
Extract the red color from the H-channel. You can have a look here to find the hue range of Red color.
Merge this new H-channel with the old S and V channel.
Convert back to BGR format. Now your BGR image will contain only the Red color.

Related

OpenCV Blue Color Ranges Detection

I am extracting the blue color using OpenCV inRange(), and the code is written in C++.
My problem is my range doesn't cover the varying shades of blue, for example blue color was extracted perfectly as shown On the other hand, was not extracted as shown
The masking code
+ (UIImage *)detectFourCorners:(UIImage *)image{
cv::Mat mat;
UIImageToMat(image, mat);
// Convert input image to into BGR
cv::Mat bgr_image;
cv::cvtColor(mat, bgr_image, cv::COLOR_RGB2BGR);
// Convert input image to HSV
cv::Mat hsv_image;
cv::cvtColor(bgr_image, hsv_image, cv::COLOR_BGR2HSV);
cv::Mat mask;
// original
cv::inRange( hsv_image, cv::Scalar(100,150,0), cv::Scalar(140,255,255), mask);
cv::Mat result_blue;
cv::bitwise_and(mat,mat,result_blue,mask);
return MatToUIImage(result_blue); }
I believe I'm not covering all the needed ranges of blue, but I don't know how to get my own ranges. If anyone could help, it would be appreciated !

How to check if a foreground is surrounded by certain color?

I want to check if a foregrond is surrounded by a certain color (int this case it's green) or if surrounded by enough pixels of a certain color.
I have the image and it's mask (below are 2 examples):
I inverted the mask, converted the image surrounding the object to HSV color space and filtered by the green color:
Rect ballBBox = boundingRect(contour);
Mat ballMask(mask, ballBBox);
Mat ballImg(img, ballBBox);
Mat imgSurroundingBall;
Mat ballMaskInv;
bitwise_not(ballMask, ballMaskInv),
ballImg.copyTo(imgSurroundingBall, ballMaskInv);
Mat imgSurroundingBallHSV;
cvtColor(imgSurroundingBall, imgSurroundingBallHSV, CV_BGR2HSV);
Scalar greenLower = Scalar(35, 100, 20);
Scalar greenUpper = Scalar(70, 255, 255);
Mat areaAroundBall;
inRange(imgSurroundingBallHSV, greenLower, greenUpper, areaAroundBall);
and the result is:
Below are examples when an object is not surrounded by green:
One of the ideas that I had was to find contour (after dialating the images) that is similar to ballMask and areaAroundBall images. But, it didn't work well since I still had a lot countours and couldn't find similar ones.
Any ideas?

how to separate the channels of an RGB image in emgu?

I need to separate an RGB image into 3 channels. In the other words i need a code to do the following.
Image<Bgr, Byte> imgBgr = CvInvoke.Imread("Im.jpg").ToImage<Bgr, Byte>();
Mat blue = imgBgr.BlueChannel;
Mat green = imgBgr.GreenChannel;
Mat red = imgBgr.RedChannel;
Thanks!
There are 2 ways to do that.
Use imgBgr.Split(). It returns an array of 3 gray images that each image represents a single color channel of the original image.
Use imgBgr.Sub(color). It will subtract the color from the original image. For example, if you want to get red color only, remove green and blue, imgBgr.Sub(new Rgb(0, 255, 255)) and so on.

Drawing a colored rectangle in a grayscale image using opencv

Is it possible to draw a colored rectangle in a grayscale image using opencv.
I tried several ways but either the whole image turns grayscale or RGB.
You can't have a mixed gray and color image. You can have a look at Is there a way to have both grayscale and rgb pixels on the same image opencv C++?.
So you can't draw a colored rectangle on a grayscale CV_8UC1 image. But you can draw it on a CV_8UC3 image that shows your gray image.
You can create a CV_8UC3 gray-colored image with cvtColor(..., ..., COLOR_GRAY2BGR), and then you can draw your colored rectangle on it, e.g:
Note that this image, however, is no more of type CV_8UC1, but CV_8UC3 instead.
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat1b gray = imread("path_to_image", IMREAD_GRAYSCALE);
// Convert gray image to BGR
Mat3b grayBGR;
cvtColor(gray, grayBGR, COLOR_GRAY2BGR);
// Draw the colored rectangle
rectangle(grayBGR, Rect(10, 10, 100, 200), Scalar(0,255,0), 2);
imshow("Image with Rect", grayBGR);
waitKey();
return 0;
}
I doubt it. A grayscale image will be stored internally as one channel per pixel.
What you must do is convert the image to colour (using red = green = blue = grey value). Then you can draw any colour into the grey background. But of course the entire image then becomes a colour image, it's very unlikely there's any support for greyscale images with small areas of colour.

color a grayscale image with opencv

i'm using openNI for some project with kinect sensor. i'd like to color the users pixels given with the depth map. now i have pixels that goes from white to black, but i want from red to black. i've tried with alpha blending, but my result is only that i have pixels from pink to black because i add (with addWeight) red+white = pink.
this is my actual code:
layers = device.getDepth().clone();
cvtColor(layers, layers, CV_GRAY2BGR);
Mat red = Mat(240,320, CV_8UC3, Scalar(255,0,0));
Mat red_body; // = Mat::zeros(240,320, CV_8UC3);
red.copyTo(red_body, device.getUserMask());
addWeighted(red_body, 0.8, layers, 0.5, 0.0, layers);
where device.getDepth() returns a cv::Mat with depth map and device.getUserMask() returns a cv::Mat with user pixels (only white pixels)
some advice?
EDIT:
one more thing:
thanks to sammy answer i've done it. but actually i don't have values exactly from 0 to 255, but from (for example) 123-220.
i'm going to find minimum and maximum via a simple for loop (are there better way?), and how can i map my values from min-max to 0-255 ?
First, OpenCV's default color format is BGR not RGB. So, your code for creating the red image should be
Mat red = Mat(240,320, CV_8UC3, Scalar(0,0,255));
For red to black color map, you can use element wise multiplication instead of alpha blending
Mat out = red_body.mul(layers, 1.0/255);
You can find the min and max values of a matrix M using
double minVal, maxVal;
minMaxLoc(M, &minVal, &maxVal, 0, 0);
You can then subtract the minValue and scale with a factor
double factor = 255.0/(maxVal - minVal);
M = factor*(M -minValue)
Kinda clumsy and slow, but maybe split layers, copy red_body (make it a one channel Mat, not 3) to the red channel, merge them back into layers?
Get the same effect, but much faster (in place) with reshape:
layers = device.getDepth().clone();
cvtColor(layers, layers, CV_GRAY2BGR);
Mat red = Mat(240,320, CV_8UC1, Scalar(255)); // One channel
Mat red_body;
red.copyTo(red_body, device.getUserMask());
Mat flatLayer = layers.reshape(1,240*320); // presumed dimensions of layer
red_body.reshape(0,240*320).copyTo(flatLayer.col(0));
// layers now has the red from red_body

Resources