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.
Related
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?
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:
I'm using opencv to process some images. I have a lot of images like below. They have some very light, shadow-like color.
What is the dimension that determine the color of the pixel is very light? What color space should I use to identify those light color pixels?
Here's a fairly simple method:
img = cv2.imread('4.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img[gray > 200] = 255
As mentioned by #RickM there are various color spaces available to analyze your image. Since you are focused on removing the shade/shadow you need to focus on channels that contain brightness information and keep the color channels aside.
In this case the LAB color space turned out to be helpful. The luminance channel expressed a lot of info on the amount of brightness in the image
img = cv2.imread('4.png')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
cv2.imshow('Luminance', l)
Then I obtained a threshold and masked the result with the original image to get mask1:
ret2, th = cv2.threshold(l, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
mask1 = cv2.bitwise_and(img, img, mask = th)
cv2.imshow('mask1', mask1)
But now the background is not what you intended it to be.
I created an image with white pixels of the same image dimension (white) and masked the inverted threshold image with it to get mask2:
white = np.zeros_like(img)
white = cv2.bitwise_not(white)
mask2 = cv2.bitwise_and(white, white, mask = cv2.bitwise_not(th))
cv2.imshow('mask2', mask2)
Upon adding both these images you get he intended image:
cv2.imshow('fin_img', mask2 + mask1)
Keep in mind that this would work only for similar images provided in the question.
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.
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