I'm looking to retain only red colour pixels and darken everything else from the image. And I want to use openCv. I managed to filter red colour using the below code, thanks to SO, detect colors from object and change its color ios
// Create Mat from UIimage
cv::Mat img = [self cvMatFromUIImage:[UIImage imageNamed:#"rgb1.jpg"]];
// Convert to HSV
cv::Mat hsvImage = cvCreateImage(img.size(),8, 3);
cv::cvtColor(img, hsvImage, CV_BGR2HSV);
std::vector<cv::Mat>channels;
// splitting the channels of HSV
cv::split(hsvImage, channels);
// Getting only the hue from channels
cv::Mat hue = channels[0];
// Creating a temporary image using the hue
cv::Mat dest;
cv::Mat temp = cvCreateImage(img.size(), 8, 3);
// Giving the threshold range
cv::inRange(hsvImage, cv::Scalar(90,50,50), cv::Scalar(130,255,255), dest);
// I guess image temp Image and Original image gets merged here
// I would appreciate some explanation here
cv::merge(channels, temp);
temp.setTo(cv::Scalar(90,50,50),dest);
cv::split(temp, channels);
cv::merge(channels, dest);
// Converting the HSV Image back to BGR
cv::cvtColor(dest, hsvImage, CV_HSV2BGR);
// Converting Mat to UIImage
self.imageView.image=[self UIImageFromCVMat:hsvImage];
But I want to keep red colours as it is and darken or blur the remaining colours. I'm confused where should I make those inverse action and how to do it as well.
Any help would be appreciated.
Updated:
Code that worked for me, hope it helps someone out there.
cv::Mat img = [self cvMatFromUIImage:[UIImage imageNamed:#"rgb1.jpg"]];
cv::Mat hsvImage;
cv::cvtColor(img , hsvImage, CV_BGR2HSV);
cv::Mat mask;
cv::inRange(hsvImage, cv::Scalar(90,50,50), cv::Scalar(130,255,255), mask); // This picks red color
// cv::inRange(hsvImage, cv::Scalar(0,50,50), cv::Scalar(30,255,255), mask); // This picks blue color
self.imageView.image = [self UIImageFromCVMat:mask];
cv::Mat maskRgb;
cv::cvtColor(mask, maskRgb, CV_GRAY2BGR);
cv::Mat result;
// cv::bitwise_and(img ,maskRgb ,result); // #berak but app crashed at this line
img.copyTo(result, mask); // This line writes the new masked image over the original image, I'm not sure if thats the right way instead of bitwise_and???
self.imageView1.image = [self UIImageFromCVMat:result];
you probably don't need the split/merge pass. why not start all simple, and make a mask from the hsv image with inRange, and apply that on the image ?
cv::Mat hsvImage;
cv::cvtColor(img , hsvImage, CV_BGR2HSV);
Mat mask; // red is on the left side of the [0..180] hue range
cv::inRange(hsvImage, cv::Scalar(0,50,50), cv::Scalar(30,255,255), mask);
cv::Mat maskRgb; // make a 3channel mask
cv::cvtColor(mask, maskRgb, CV_GRAY2BGR);
Mat result;
bitwise_and(img ,maskRgb ,result);
Related
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 !
I'm using Opencv 3.0 to get only the colored objects in an image. Therefore i create and use a mask.
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
namedWindow("Display", CV_WINDOW_AUTOSIZE);
namedWindow("Orignial", CV_WINDOW_AUTOSIZE);
namedWindow("Mask", CV_WINDOW_AUTOSIZE);
// First load your image
Mat mSrc = imread("IMG_0005_AUSZUG2.png", CV_LOAD_IMAGE_COLOR);
Mat mGray = Mat::zeros(mSrc.size(), mSrc.type());
cvtColor(mSrc, mGray, CV_BGR2GRAY);
// define your mask
Mat mask = Mat::zeros(mSrc.size(), mSrc.type());
// define destination image
Mat dstImg = Mat::zeros(mSrc.size(), mSrc.type());
//finding mask
inRange(mSrc, Scalar(90, 90, 90), Scalar(180, 180, 180), mask);
// combination of mask and Source image
dilate(mask, mask, Mat(), Point(-1, -1));
bitwise_not(mask, mask);
//cvtColor(mask, mask, CV_GRAY2BGR);
mSrc.copyTo(dstImg, mask);
//bitwise_and(mSrc, mSrc, dstImg, mask);
imshow("Mask", mask);
imshow("Orignial", mSrc);
imshow("Display", dstImg);
waitKey(0);
return 0;
}
As you can see the result image is not the intended one. Only the colored objects should stay, because they have a white background in the mask, but it seems that the result image is a combination of source and mask.
Anybody know how to fix this ?
Source:
Mask:
Result:
To understand your requirement- you have an image with some coloured objects in it, in a white background, and you essentially want an result image containing the same coloured objects in a black background instead.
If that's the case, inRange will not help because you've essentially kept the threshold between grey values 90 and 180, so your code will discard dark objects as well.
To ensure that you obtain a mask that is black only in the white background regions, I would suggest using the threshold function instead, as shown:
//finding mask
//inRange(mSrc, Scalar(90, 90, 90), Scalar(180, 180, 180), mask);
threshold(mGray, mask, 220, 255, THRESH_BINARY_INV);
This function will ensure that any pixel value in your greyscale image above 220 will be set to 0 in the binary mask.
To superimpose the binary mask over the source image, you should use the subtract method, as shown:
cvtColor(mask,mask,CV_GRAY2BGR);//change thresh to a 3 channel image
Mat mResult = Mat::zeros(mSrc.size(), mSrc.type());
subtract(mask,mSrc,mResult);
subtract(mask,mResult,mResult);
I have got a mask calculated in grab_cut(which calculates the foreground). I want to extract only the background leaving the foreground transparent. I manage to do so using the following code in order to extract foreground(background transparent). How is it possible to do the opposite?
int border = 20;
int border2 = border + border;
cv::Rect rectangle(border,border,image.cols-border2,image.rows-border2);
cv::Mat result; // segmentation result (4 possible values)
cv::Mat bgModel,fgModel; /
cv::grabCut(image, // input image
result, // segmentation result
rectangle,// rectangle containing foreground
bgModel,fgModel, // models
1, // number of iterations
cv::GC_INIT_WITH_RECT); // use rectangle
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(foreground,result); // bg pixels not copied
cv::rectangle(image, rectangle, cv::Scalar(255,255,255),1);
cv::imwrite(argv[2], foreground);
cv::imwrite(argv[3], image);
Mat dst;//(src.rows,src.cols,CV_8UC4);
Mat tmp,alpha;
cvtColor(foreground,tmp,CV_BGR2GRAY);
threshold(tmp,alpha,100,255,THRESH_BINARY);
Mat rgb[3];
split(foreground,rgb);
Mat rgba[4]={rgb[0],rgb[1],rgb[2],alpha};
merge(rgba,4,dst);
imwrite("dst.png",dst);
Basically i think I ve got to change those lines:
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(foreground,result); // bg pixels not copied
How is is possible to select the rest of the image the opposite of result?
Just invert your mask as in:
cv::Mat background(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(background, ~result); // fg pixels not copied
I'm getting a strange exception (exception: cv::Exception at memory location 0x002EB6CC) when I try to convert a RGB image to Grayscale. Can someone help me?
const cv::Mat img1 = cv::imread(filename, 0)
cv::Mat gs_rgb(img1.size(), CV_8UC1);
cv::cvtColor(img1, gs_rgb, CV_RGB2GRAY);
You are loading image as gray scale and trying to convert the gray scale to gray scale again.
The line
const cv::Mat img1 = cv::imread(filename, 0)
will load the image
where the second argument
=0->CV_LOAD_IMAGE_GRAYSCALE->load gray scale
=1->CV_LOAD_IMAGE_COLOR->load color
<0->CV_LOAD_IMAGE_ANYDEPTH->Return the loaded image as is (with alpha channel).
So either load image as gray scale and use it like,
const cv::Mat img1 = cv::imread(filename, 0)//load gray
Or load it as color and then convert to gray scale like,
const cv::Mat img1 = cv::imread(filename, 1);//load color
Mat gray;//no need of allocation, will allocate automatically.
cv::cvtColor(img1,gray, CV_BGR2GRAY);//opencv default color order is BGR
See more info here in imread documentation.
I want to detect a specific color say, blue, from a live video stream.
I have written the following code which displays the live video stream and change it into HSV and grayscale. Since I am completely new to opencv I have no idea what to do next.
Can someone complete the code for me to detect a specific color.
#include<opencv\cv.h>
#include<opencv\highgui.h>
using namespace cv;
int main(){
Mat image;
Mat gray;
Mat hsv;
VideoCapture cap;
cap.open(0);
namedWindow("window", CV_WINDOW_AUTOSIZE);
namedWindow("gray", CV_WINDOW_AUTOSIZE);
namedWindow("hsv", CV_WINDOW_AUTOSIZE);
while (1){
cap >> image;
cvtColor(image, gray, CV_BGR2GRAY);
cvtColor(image, hsv, CV_BGR2HSV);
imshow("window", image);
imshow("gray", gray);
imshow("hsv", hsv);
waitKey(33);
}
return 0;
}
You can do this in three steps:
Load frame.
Convert BGR to HSV color space.
Inrange between the color range to detect.
Edit
You can use this code to find the HSV value of any pixel from your source image. You can see a good explanation about HSV color space here, download the HSV colour wheel from there and manually find out the HSV range.
The following range can be used for the image supplied in the comments:
hsv_min--> (106,60,90)
hsv_max-->(124,255,255)
The following code can be used:
Mat src=imread("image.jpg");
Mat HSV;
Mat threshold;
cvtColor(src,HSV,CV_BGR2HSV);
inRange(HSV,Scalar(106,60,90),Scalar(124,255,255),threshold);
imshow("thr",threshold);
This is the input:
This is the output: