OpenCV: transforming 3 channel image into 4 channel - c++

I am trying to change 3-channel image into 4-channel like this:
cv::VideoCapture video;
video.open("sample.avi");
cv::Mat source;
cv::Mat newSrc;
int from_to = { 0,0, 1,1, 2,2, 3,3 };
for ( int i = 0; i < 1000; i ++ )
{
video >> source;
cv::mixChannels ( source, 2, newSrc, 1, from_to, 4 );
}
Then I got
too many input arguments in function call
for the 'mixChannels' line. Besides, I am not sure whether I am giving the arguments correctly for my goal. Can someone help me? Thank you.

You can convert 3 channel image to 4 channel as follows:
cv::Mat source = cv::imread(path);
cv::Mat newSrc(source.size(), CV_MAKE_TYPE(source.depth(), 4));
int from_to[] = { 0,0, 1,1, 2,2, 2,3 };
cv::mixChannels(&source,1,&newSrc,1,from_to,4);
This way channel 4 will be a duplicate of channel 3. By using a negative number in the from_to list, the output channel is zero filled. eg:
int from_to[] = { 0,0, 1,1, 2,2, -1,3 };

What is the 4th channel supposed to contain?
How about:
VideoCapture cap(0);
Mat frame;
cap >> frame;
Mat RGBA(frame.size(), CV_8UC4, camData);
cv::cvtColor(frame, RGBA, CV_BGR2RGBA, 4);

I think it should be like this:
cv::Mat source = cv::imread(path);
cv::Mat newSrc = cv::Mat(source.rows,source.cols,CV_8UC4);
int from_to[] = { 0,0, 1,1, 2,2, 3,3 };
cv::mixChannels(&source,1,&newSrc,1,from_to, source.channels());
In C++11 you can use initializer lists to provide multiple matrices for batch conversion inline:
cv::mixChannels({{source}}, {{newSrc}}, from_to, source.channels());
We set 3 pairs to be copied, so this leaves the 4 channel empty in newsrc. And 1 in the second and forth parameter means that the pointers source and newSrc point to one element to be processed. The last parameter gives the length of from_to.

Related

Convert from RGB to YUYV in OpenCV

Is there a way to convert from RGB to YUYV (YUY 4:2:2) format? I noted that OpenCV has reverse operation, but not RGB to YUYV for some reason. Maybe someone can point to code which does that (even outside of OpenCV library)?
UPDATE
I found libyuv library which may work for this purpose by doing BGR to ARGB conversion and then ARGB to YUY2 format (hopefully this is the same as YUYV 4:2:2). But it doesn't seem to work. Do you happen to know what yuyv buffer dimensions/type should look like? What its stride?
To clarify YUYV and YUY2 are the same formats if it helps.
UPDATE 2
Here is my code of using libyuv library:
Mat frame;
// Convert original image im from BGR to BGRA for further use in libyuv
cvtColor(im, frame, CVX_BGR2BGRA);
// Actually libyuv requires ARGB (i.e. reverse of BGRA), so I swap channels here
int from_to[] = { 0,3, 1,2, 2,1, 3,0 };
mixChannels(&frame, 1, &frame, 1, from_to, 4);
// This is the most confusing part. Not sure what argb_stride suppose to be - length of a row in bytes or size of single value in the array?
const uint8_t* argb_data = frame.data;
int argb_stride = 8;
// Also it is not clear what size of yuyv frame should be since we duplicate one Y
Mat yuyv(frame.rows, frame.cols, CVX_8UC2);
uint8_t* yuyv_data = yuyv.data;
int yuyv_stride = 16;
// Do actual conversion
libyuv::ARGBToYUY2(argb_data, argb_stride, yuyv_data, yuyv_stride,
frame.cols, frame.rows);
// Then I feed yuyv_data to video stream buffer and see green or purple image instead of video stream.
UPDATE 3
Mat frame;
cvtColor(im, frame, CVX_BGR2BGRA);
// ARGB
int from_to[] = { 0,3, 1,2, 2,1, 3,0 };
Mat rgba(frame.size(), frame.type());
mixChannels(&frame, 1, &rgba, 1, from_to, 4);
const uint8_t* argb_data = rgba.data;
int argb_stride = rgba.cols*4;
Mat yuyv(rgba.rows, rgba.cols, CVX_8UC2);
uint8_t* yuyv_data = yuyv.data;
int yuyv_stride = width * 2;
int res = libyuv::ARGBToYUY2(argb_data, argb_stride, yuyv_data, yuyv_stride, rgba.cols, rgba.rows);
It appears that although method is called ARGBToYUY2 it requires BGRA order of channels (not reverse).

countNonZero function gives an assertion error in openCV

I tried to get horizontal projection using countNonZero() function as below.
Mat src = imread(INPUT_FILE, CV_LOAD_IMAGE_COLOR);
Mat binaryImage = src.clone();
cvtColor(src, src, CV_BGR2GRAY);
Mat horizontal = Mat::zeros(1,binaryImage.cols, CV_8UC1);
for (int i = 0; i<binaryImage.cols; i++)
{
Mat roi = binaryImage(Rect(0, 0, 1, binaryImage.rows));
horizontal.at<int>(0,i) = countNonZero(roi);
cout << "Col no:" << i << " >>" << horizontal.at<int>(0, i);
}
But an error is occured in the line of calling countonZero() function. Error is as follows.
OpenCV Error: Assertion failed (src.channels() == 1 && func != 0) in cv::countNo
nZero, file C:\builds\2_4_PackSlave-win32-vc12-shared\opencv\modules\core\src\st
at.cpp, line 549
Can somebody please point out the mistake?
Assertion src.channels() == 1 means that image should have 1 channel, i.e. it has to be gray, not colored. You are calling countNonZero on roi, which is a subimage of binaryImage, which is a clone of src, which is originally colored.
I suppose you wanted to write cvtColor(binaryImage, binaryImage, CV_BGR2GRAY);. In this case it makes sense. However, I do not see you using src anywhere again, so perhaps you do not need this intermediate image. In case you do, do not call "binary", since "binary" in computer vision usually stands for black-or-white image, only two colors. Your image is "gray", since it has all shades of black and white.
Concerning your original task, Miki is right, you should use cv::reduce for it. He already gave you an example on how to use it.
BTW, you can compute horizontal projection using reduce giving as argument CV_REDUCE_SUM.
A minimal example:
Mat1b mat(4, 4, uchar(0));
mat(0,0) = uchar(1);
mat(0,1) = uchar(1);
mat(1,1) = uchar(1);
// mat is:
//
// 1100
// 0100
// 0000
// 0000
// Horizontal projection, result would be a column matrix
Mat1i reducedHor;
cv::reduce(mat, reducedHor, 1, CV_REDUCE_SUM);
// reducedHor is:
//
// 2
// 1
// 0
// 0
// Vertical projection, result would be a row matrix
Mat1i reducedVer;
cv::reduce(mat, reducedVer, 0, CV_REDUCE_SUM);
// reducedVer is:
//
// 1200
// Summary
//
// 1100 > 2
// 0100 > 1
// 0000 > 0
// 0000 > 0
//
// vvvv
// 1200
You can use this with your images like this:
// RGB image
Mat3b img = imread("path_to_image");
// Gray image, contains values in [0,255]
Mat1b gray;
cvtColor(img, gray, CV_BGR2GRAY);
// Binary image, contains only 0,1 values
// The sum of pixel values will equal the count of non-zero pixels
Mat1b binary;
threshold(gray, binary, 1, 1, THRESH_BINARY);
// Horizontal projection
Mat1i reducedHor;
cv::reduce(binary, reducedHor, 1, CV_REDUCE_SUM);
// Vertical projection
Mat1i reducedVer;
cv::reduce(binary, reducedVer, 0, CV_REDUCE_SUM);

opencv: different ways to fill a cv::mat

I know that for fill a cv::Mat there is the nice cv::Mat::setTo method but I don't understand why I don't have the same effect with those pieces of code:
// build the mat
m = cv::Mat::zeros(size, CV_8UC3);
cv::cvtColor(m, m, CV_BGR2BGRA); // add alpha channel
/////////////////////////////////////////////////////////// this works
m.setTo( cv::Scalar(0,144,0,55) );
m = cv::Mat::zeros(size, CV_8UC3);
cv::cvtColor(m, m, CV_BGR2BGRA);
/////////////////////////////////////////////////////////// this does NOT work
m = m + cv::Scalar(0,144,0,55)
m = cv::Mat::ones(size, CV_8UC3);
cv::cvtColor(m, m, CV_BGR2BGRA);
/////////////////////////////////////////////////////////// this does NOT work
m = m.mul( cv::Scalar(0,144,0,55) );
m = cv::Mat::zeros(size, CV_8UC3);
cv::cvtColor(m, m, CV_BGR2BGRA);
/////////////////////////////////////////////////////////// this works too!
cv::rectangle(tracks,
cv::Rect(0, 0, tracks.cols, tracks.rows),
cv::Scalar(0,144,0,55),
-1);
PS: I'm displaying those mats as an OpenGL alpha texture
I guess "not work" means that the output is not the same as using setTo?
When transforming with cv::cvtColor, the alpha-channel is initialized to 255. If you add or multiply anything it will stay at 255.
Why do you use cv::cvtColor to transform instead of just using CV_8UC4 when creating the mat?
You can't use cv::Mat::ones for multichannel initialization. Only the first channel is set to 1 when using cv::Mat::ones. Use cv::Mat( x, y, CV_8UC3, CV_RGB(1,1,1) ).
For an aplha channel you need to use CV_8UC4, not CV_8UC3.

Convert Image Color from Grayscale to RGB OpenCV C++

Basically I am trying to convert the below output image to color(RGB). The image that this code currently outputs is grayscale, however, for my application I would like it to be output as color. Please let me know where I should convert the image.
Also the code below is C++ and it using a function from openCV. Please keep in mind that I am using a wrapper to use this code in my iphone application.
cv::Mat CVCircles::detectedCirclesInImage(cv::Mat img, double dp, double minDist, double param1, double param2, int min_radius, int max_radius) {
//(cv::Mat img, double minDist, int min_radius, int max_radius)
if(img.empty())
{
cout << "can not open image " << endl;
return img;
}
Mat cimg;
medianBlur(img, img, 5);
cvtColor(img, cimg, CV_GRAY2RGB);
vector<Vec3f> circles;
HoughCircles( img //InputArray
, circles //OutputArray
, CV_HOUGH_GRADIENT //int method
, 1//dp //double dp=1 1 ... 20
, minDist //double minDist=10 log 1...1000
, 100//param1 //double param1=100
, 30//param2 //double param2=30 10 ... 50
, min_radius //int minRadius=1 1 ... 500
, max_radius //int maxRadius=30 1 ... 500
);
for( size_t i = 0; i < circles.size(); i++ )
{
Vec3i c = circles[i];
circle( cimg, Point(c[0], c[1]), c[2], Scalar(255,0,0), 3, CV_AA);
circle( cimg, Point(c[0], c[1]), 2, Scalar(0,255,0), 3, CV_AA);
}
return cimg;
}
This is currently set up to expect a grayscale image as input. I think that you are asking how to adapt it to accept a colour input image and return a colour output image. You don't need to change much:
cv::Mat CVCircles::detectedCirclesInImage(cv::Mat img, double dp, double minDist, double param1, double param2, int min_radius, int max_radius) {
if(img.empty())
{
cout << "can not open image " << endl;
return img;
}
Mat img;
if (img.type()==CV_8UC1) {
//input image is grayscale
cvtColor(img, cimg, CV_GRAY2RGB);
} else {
//input image is colour
cimg = img;
cvtColor(img, img, CV_RGB2GRAY);
}
the rest stays as is.
If your input image is colour, you are converting it to gray for processing by HoughCircles, and applying the found circles to the original colour image for output.
The cvtImage routine will simply copy your gray element to each of the three elements R, G, and B for each pixel. In other words if the pixel gray value is 26, then the new image will have R = 26, G = 26, B = 26.
The image presented will still LOOK grayscale even though it contains all 3 color components, all you have essentially done is to triple the space necessary to store the same image.
If indeed you want color to appear in the image (when you view it), this is truly impossible to go from grayscale back to the ORIGINAL colors. There are however means of pseudo-coloring or false coloring the image.
http://en.wikipedia.org/wiki/False_color
http://blog.martinperis.com/2011/09/opencv-pseudocolor-and-chroma-depth.html
http://podeplace.blogspot.com/2012/11/opencv-pseudocolors.html
The code you have pasted is returning colored image.
You are already doing cvtColor(img, cimg, CV_GRAY2RGB), and then I don't see cimg getting converted to grayscale anywhere !, To verify it try displaying it before returning from this function :
imshow("c",cimg);
waitKey(0);
return cimg;
You can draw circles to the input color image.
Check the documentation given in the openCV
http://docs.opencv.org/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.html

OpenCV 2.4 Jpeg to PNG with alpha channel

I have a JPEG and a Mask. I want to create a PNG with the three JPEG channels and the alpha channel should be the Mask. How can I achieve this with OpenCV?
Regards
std::vector<cv::Mat> channels;
cv::split(jpgImage, channels);
channels.push_back(mask);
cv::Mat bgraImage;
cv::merge(channels, bgrAImage);
Documentation for split and merge functions
Thanks for your answer, I found a second solution:
cv::Mat transparent( height, width, CV_8UC4);
cv::Mat srcImg[] = {JPEG_img, alpha_Mask};
int from_to[] = { 0,0, 1,1, 2,2, 3,3 };
cv::mixChannels( srcImg, 2, &transparent, 1, from_to, 4 );
This works perfect, not sure which solution is better.