Opencv create new image using cv::Mat - c++

I'm new to opencv and i'm trying on some sample codes.
in one code, Mat gr(row1,col1,CV_8UC1,scalar(0));
int x = gr.at<uchar> (row,col);
And in another one,
Mat grHistrogram(301,260,CV_8UC1,Scalar(0,0,0));
line(grHistrogram,pt1,pt2,Scalar(255,255,255),1,8,0);
Now my question is if i used scalar(0) instead of scalar(0,0,0) in second code, The code doesn't work.
1.Why this happening since, Both create a Mat image structure.
2.what is the purpose of const cv:Scalar &_s.
I search the Documentaion from Opencv site (opencv.pdf,opencv2refman.pdf) and Oreilly's Opencv book. But couldn't find a explained answer.
I think i'm using the Mat(int _rows,int _cols,int _type,const cv:Scalar &_s) struct.

First, you need the following information to create the image:
Width: 301 pixels
Height: 260 pixels
Each pixel value (intensity) is 0 ~ 255: an 8-bit unsigned integer
Supports all RGB colors: 3 channels
Initial color: black = (B, G, R) = (0, 0, 0)
You can create the Image using cv::Mat:
Mat grHistogram(260, 301, CV_8UC3, Scalar(0, 0, 0));
The 8U means the 8-bit Usigned integer, C3 means 3 Channels for RGB color, and Scalar(0, 0, 0) is the initial value for each pixel. Similarly,
line(grHistrogram,pt1,pt2,Scalar(255,255,255),1,8,0);
is to draw a line on grHistogram from point pt1 to point pt2. The color of line is white (255, 255, 255) with 1-pixel thickness, 8-connected line, and 0-shift.
Sometimes you don't need a RGB-color image, but a simple grayscale image. That is, use one channel instead of three. The type can be changed to CV_8UC1 and you only need to specify the intensity for one channel, Scalar(0) for example.
Back to your problem,
Why this happening since, both create a Mat image structure?
Because you need to specify the type of the Mat. Is it a color image CV_8UC3 or a grayscale image CV_8UC1? They are different. Your program may not work as you think if you use Scalar(255) on a CV_8UC3 image.
What is the purpose of const cv:Scalar &_s ?
cv::Scalar is use to specify the intensity value for each pixel. For example, Scalar(255, 0, 0) is blue and Scalar(0, 0, 0) is black if type is CV_8UC3. Or Scalar(0) is black if it's a CV_8UC1 grayscale image. Avoid mixing them together.

You can create single channel image or multi channel image.
creating single channel image : Mat img(500, 1000, CV_8UC1, Scalar(70));
creating multi channel image : Mat img1(500, 1000, CV_8UC3, Scalar(10, 100, 150));
you can see more example and detail from following page.
https://progtpoint.blogspot.com/2017/01/tutorial-3-create-image.html

Related

Access pixel value of mask using opencv

I got a problem where I need to access pixels of a opencv Mat image container.
I use opencv inRange function to create a mask. In that mask I need to check the value of different pixels, but I won't receive the values I expect to receive.
// convert image to hsv for better color-detection
cv::Mat img_hsv, maskR, maskY, mask1, mask2;
cv::cvtColor(image, img_hsv, cv::COLOR_BGR2HSV);
// Gen lower mask (0-5) and upper mask (175-180) of RED
cv::inRange(img_hsv, cv::Scalar(0, 50, 20), cv::Scalar(5, 255, 255), mask1);
cv::inRange(img_hsv, cv::Scalar(175, 50, 20), cv::Scalar(180, 255, 255), mask2);
// Merge the masks
cv::bitwise_or(mask1, mask2, maskR);
after that I try to read the pixel values where I got extremely high values and even nans, but most of them zeros, which is expected as the mask is only black and white
if (maskR.at<double>(position.x, position.y) == 255)
is there something I'm missing? I tried with double, uchar, int and float
when I print the mask, I can clearly see the 0 and 255 entries(no nans or strange numbers), but when I access them with the at() function, I wont get the same results.
The coordinates of the pixels should be in the range of the Mat as the dimension of the mask is 1080x1920 and non of the coordinates reach over that.
I got the dimension by using cv::size
I finally found the answer to my own question.
It works when I use uchar:
maskR.at<uchar>(position.x, position.y) == 255
I thought this wouldn't work because printing this with std::cout wouldn't give me an output, but the reason for that is that I forgot to cast uchar so it could be printed in the console

Copy image to section of another image

I am attempting to copy an image to another image (place a logo in the bottom right side of an image). My code successfully copies the logo onto the room image but it does some weird stretching when I apply a mask.
// Overlay logo
// src = 3 channel image of a room
// logo = 3 channel image of a logo (most of the Mat is black except for the logo).
// logo is smaller than the room image
Mat res;
Mat thresh;
src.copyTo(res);
threshold(logo, thresh, 1, 255, CV_THRESH_BINARY);
Rect r(res.cols-logo.cols, res.rows-logo.rows, logo.cols, logo.rows);
logo.copyTo(res(r), thresh);
My result:
Mask is:
Your logo seems to be a color image. Which means that your thresh image is also a color image. When you use it as a mask, it's going to be horizontally stretched because the extra channels are interpreted as more columns.
Try adding cvtColor(logo, grayLogo, CV_BGR2GRAY) before the threshold and use the grayLogo in the threshold.
Mask size and image size are not equal (thats why mask is streched). To solve this, you should create mask image and copy your threshed logo in good position.
[EDIT]: According to sunreef's answer - three channels in mask could also be a source of your problem.
Code posted below worked for me.
cv::Mat t_roomImage = cv::imread("E:\\Workspace\\KS\\misc\\M4nKr.jpg");
cv::Mat t_logoImage = cv::imread("E:\\Workspace\\KS\\misc\\7sFbv.jpg",0);
cv::threshold(t_logoImage, t_logoImage, 10, 255, CV_THRESH_BINARY);
cv::Mat t_mask = cv::Mat::zeros(t_roomImage.size(), CV_8U);
cv::Rect t_rect = cv::Rect(t_mask.cols - t_logoImage.cols, t_mask.rows -
t_logoImage.rows, t_logoImage.cols, t_logoImage.rows);
t_logoImage.copyTo(t_mask(t_rect));
t_roomImage.setTo(cv::Scalar(255, 0, 0), t_mask);
I used setTo, because I had no logo image. There you can use copyTo instead.
Results:

OpenCV - segmenting tree from the image

I am trying to segment the color green in the HSV-color space. I have this image of a tree and I would only like the upper part of the tree to be left.
This is the image I am starting from and the mask I obtain is just an entirely black image
This is my current code:
Mat input = imread(image_location);
imshow("input img",input); waitKey(0);
//convert image to HSV
Mat input_hsv;
cvtColor(input,input_hsv,COLOR_BGR2HSV);
vector<Mat>channels;
split(input_hsv, channels);
Mat H = channels[0];
Mat S = channels[1];
Mat V = channels[2];
Mat mask2;
inRange(input_hsv, Scalar(70, 0, 0), Scalar(143, 255, 255), mask2);
imshow("mask2", mask2);waitKey(0);
Normally the color green in HSV ranges from +/- 70 to 140.
But it doesn't seem to work at all. Could somebody help?
You are working in 8U. Thus, the H component which is normally in degrees [0,360) is compressed to fit 255 by halving.
See docs: 8-bit images: V←255V,S←255S,H←H/2(to fit to 0 to 255)
So the original H green range [70,140] should be halved to [35,70].

Splitting a color image into three channels using Open CV and C++?

I am new to Open CV, so please forgive me if my question sounds stupid. So, i was studying about this new concept of splitting a BGR channel to individual channels using the split function. I was reading this article(http://answers.opencv.org/question/37132/i-want-to-split-and-show-r-b-g-pictures-why-does-it-not-work/) and i could not understand the code. So, please can anyone explain me the following line of code as i am really wanted to understand the concept.
I did not understand the create blue channel part at all. Please can anyone explain me a bit?
src = imread("pic.png");
vector<mat> spl(3);
split(src,spl);
Mat empty_image = Mat::zeros(src.rows, src.cols, CV_8UC1);
Mat result_blue(src.rows, src.cols, CV_8UC3); // notice the 3 channels here!
// Create blue channel
Mat in1[] = { spl[0], empty_image, empty_image };
int from_to1[] = { 0,0, 1,1, 2,2 };
mixChannels( in1, 3, &result_blue, 1, from_to1, 3 );
imshow("blue image", result_blue);
What the code does is split the color image into 3 grayscale images, holding the intensities of the red, green and blue.
Then the code takes the blue channel and constructs a color image with a zero red and green, so that when you show it, it will show as bluish, and not just grayscale.
Th OpenCV Split Function:
cv:split(src, spl)
Takes a 3 channel 24bit (8 bit for each channel) of type CV_8UC3 and split it into type CV_8UC1 (GrayScale), that is a single channel image or type R, G and B separately.
If you take as an example a Green channel image from spl1 vector and do cv::imshow, you should notice that the green color from the original RGB image will appear as high intensity value in only Green channel image. Likewise for any other channel.
mixChannels( in1, 3, &result_blue, 1, from_to1, 3 );
mixChannel is basically, a function that copies some channel of source image to the new destination image. When using the function you need to specify the the number of channel you need to mix which in your case is 3.
cv::Mat result_blue(src.rows, src.cols, CV_8UC3);
Note that this variable of type cv::Mat is memory to hold the output image where the channels from src are mixed to the destination image.
1 specifies the number of matrix or image you want in the final output image, that is in cv::Mat result_blue.
int from_to1[] = { 0,0, 1,1, 2,2 };
This array specifies which channel from src needs to be mixed with which channel in the destination. That is 0 will be copied to 0 and so on.
The last param 3 in mixChannel specifies the number of channel pairs in the destination image.
You may also look at this function of merging channels.
The cv::merge() function on the other than takes multiple single channel image and merge it to produce a high level. Note that cv::merge also takes a int, the second param which specifies the number of channels you need to merge.
Lastly, I would suggest that you play with the function to understand them well.

Getting masked area to be transparent?

So far i have managed to use masks and get the second image from the first. But what i want is the black area in second image to be transparent (i.e the output i an trying to get is the third image) Here is the code so far. Please advice me on this.
EDIT: Third one is from photoshop
//imwrite parameters
compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
compression_params.push_back(100);
//reading image to be masked
image = imread(main_img, -1);
//CV_LOAD_IMAGE_COLOR
namedWindow("output", WINDOW_NORMAL);
//imshow("output", image);
//Creating mask image with same size as original image
Mat mask(image.rows, image.cols, CV_8UC1, Scalar(0));
// Create Polygon from vertices
ROI_Vertices.push_back(Point2f(float(3112),float(58)));
ROI_Vertices.push_back(Point2f(float(3515),float(58)));
ROI_Vertices.push_back(Point2f(float(3515),float(1332)));
ROI_Vertices.push_back(Point2f(float(3112),float(958)));
approxPolyDP(ROI_Vertices, ROI_Poly, 1, true);
// Fill polygon white
fillConvexPoly(mask, &ROI_Poly[0] , ROI_Poly.size(), 255, 8, 0);
//imshow("output", mask);
// Create new image for result storage
imageDest = cvCreateMat(image.rows, image.cols, CV_8UC4);
// Cut out ROI and store it in imageDest
image.copyTo(imageDest, mask);
imwrite("masked.jpeg", imageDest, compression_params);
imshow("output", imageDest);
cvWaitKey(0);
This can be done by first setting its alpha value to 0 of the regions that you want to make them fully transparent (255 for others), and then save it to PNG.
To set the alpha value of pixel-(x,y), it can be done:
image.at<cv::Vec4b>(y, x)[3] = 0;
PS: you need to convert it to 4-channel format first if the image is not currently. For example:
cv::cvtColor(image, image, CV_BGR2BGRA);
Updated: It will be easier if you have already computed the mask for the ROI region, where you can simply merge it with the original image (assume having 3 channels) to get the final result. Like:
cv::Mat mask; // 0 for transparent regions, 255 otherwise (serve as the alpha channel)
std::vector<cv::Mat> channels;
cv::split(image, channels);
channels.push_back(mask);
cv::Mat result;
cv::merge(channels, result);