How to increment yellow pixels in RGB image - opencv

I have an image stored in RGB color space and I need to detect yellow pixel and increment each one by 5.
For example, if I have a photo with a yellow lemon and a brown table, I have to turn the lemon more yellow and the table must remain the same.
Then I have to save the new image.
How can I perform it with openCV and C++?

Yes.
Convert image into HSV color space.
Calculate yellow range in HSV (from Scalar to Scalar).
Create binary mask for yellow: inRange.
Call add with mask from (3) for your HSV image and cv::Scalar(5, 0, 0)
Convert Result to RGB.
Example:
cv::Mat rgbImg = cv::imread("src.jpg", cv::IMREAD_COLOR);
cv::Mat hsvImg;
cv::cvtColor(rgbImg, hsvImg, cv::COLOR_BGR2HSV);
cv::Mat threshImg;
cv::inRange(hsvImg, cv::Scalar(20, 100, 100), cv::Scalar(30, 255, 255), threshImg);
cv::imwrite("thresh.png", threshImg);
cv::add(hsvImg, cv::Scalar(5, 0, 0), hsvImg, threshImg);
cv::cvtColor(hsvImg, rgbImg, cv::COLOR_HSV2BGR);
cv::imwrite("res.png", rgbImg);
And pictures:

Related

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.

iOS: OpenCV Red color range

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.

OpenCV - Floodfill onto new Mat

Given a point on an image, I'd like to floodfill all points connected to that point - but onto a new image. A naive way to do this would be to floodfill the original image to a special magic colour value. Then, visit each pixel, and copy all pixels with this magic colour value to the new image. There must be a better way!
Why don't you use the second variant of cv::floodFill to create a mask?
int floodFill(InputOutputArray image, InputOutputArray mask, Point
seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar
upDiff=Scalar(), int flags=4 )
Original image
cv::Mat img = cv::imread("squares.png");
First variant
cv::floodFill(img, cv::Point(150,150), cv::Scalar(255.0, 255.0, 255.0));
This is the img
Second variant
cv::Mat mask = cv::Mat::zeros(img.rows + 2, img.cols + 2, CV_8U);
cv::floodFill(img, mask, cv::Point(150,150), 255, 0, cv::Scalar(), cv::Scalar(),
4 + (255 << 8) + cv::FLOODFILL_MASK_ONLY);
This is the mask. img doesn't change
If you go with this though, note that:
Since the mask is larger than the filled image, a pixel (x,y) in image corresponds to the pixel (x+1, y+1) in the mask.

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