Assign 3x1 mat to 3 channels mat - c++

This question is continuance from my question in this link. After i get mat matrix, the 3x1 matrix is multiplied with 3x3 mat matrix.
for (int i = 0; i < im.rows; i++)
{
for (int j = 0; j < im.cols; j++)
{
for (int k = 0; k < nChannels; k++)
{
zay(k) = im.at<Vec3b>(i, j)[k]; // get pixel value and assigned to Vec4b zay
}
//convert to mat, so i can easily multiplied it
mat.at <double>(0, 0) = zay[0];
mat.at <double>(1, 0) = zay[1];
mat.at <double>(2, 0) = zay[2];
We get 3x1 mat matrix and do multiplication with the filter.
multiply= Filter*mat;
And i get mat matrix 3x1. I want to assign the value into my new 3 channels mat matrix, how to do that? I want to construct an images using this operation. I'm not use convolution function, because i think the result is different. I'm working in c++, and i want to change the coloured images to another color using matrix multiplication. I get the algorithm from this paper. In that paper, we need to multiplied several matrix to get the result.

OpenCV gives you a reshape function to change the number of channels/rows/columns implicitly:
http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-reshape
This is very efficient since no data is copied, only the matrix header is changed.
try:
cv::Mat mat3Channels = mat.reshape(3,1);
Didn't test it, but should work. It should give you a 1x1 matrix with 3 channel element (Vec3d) if you want a Vec3b element instead, you have to convert it:
cv::Mat mat3ChannelsVec3b;
mat3Channels.convertTo(mat3ChannelsVec3b, CV_8UC3);
If you just want to write your mat back, it might be better to create a single Vec3b element instead:
cv::Vec3b element3Channels;
element3Channels[0] = multiply.at<double>(0,0);
element3Channels[1] = multiply.at<double>(1,0);
element3Channels[2] = multiply.at<double>(2,0);
But care in all cases, that Vec3b elements can't save values < 0 and > 255
Edit: After reading your question again, you ask how to assign...
I guess you have another matrix:
cv::Mat outputMatrix = cv::Mat(im.rows, im.cols, CV_8UC3, cv::Scalar(0,0,0));
Now to assign multiply to the element in outputMatrix you ca do:
cv::Vec3b element3Channels;
element3Channels[0] = multiply.at<double>(0,0);
element3Channels[1] = multiply.at<double>(1,0);
element3Channels[2] = multiply.at<double>(2,0);
outputMatrix.at<Vec3b>(i, j) = element3Channels;
If you need alpha channel too, you can adapt that easily.

Related

OpenCV - How to apply Kmeans on a grayscale image?

I am trying to cluster a grayscale image using Kmeans.
First, I have a question:
Is Kmeans the best way to cluster a Mat or are there newer more efficient approaches?
Second, when I try this:
Mat degrees = imread("an image" , IMREAD_GRAYSCALE);
const unsigned int singleLineSize = degrees.rows * degrees.cols;
Mat data = degrees.reshape(1, singleLineSize);
data.convertTo(data, CV_32F);
std::vector<int> labels;
cv::Mat1f colors;
cv::kmeans(data, 3, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.), 2, cv::KMEANS_PP_CENTERS, colors);
for (unsigned int i = 0; i < singleLineSize; i++) {
data.at<float>(i) = colors(labels[i]);
}
Mat outputImage = data.reshape(1, degrees.rows);
outputImage.convertTo(outputImage, CV_8U);
imshow("outputImage", outputImage);
The result (outputImage) is empty.
When I try to multiply colors in the for loop like data.at<float>(i) = 255 * colors(labels[i]);
I get this error:
Unhandled exception : Integer division by zero.
How can I cluster a grayscale image properly?
It looks to me that you are wrongly parsing the labels and colors info to your output matrix.
K-means returns this info:
Labels - This is an int matrix with all the cluster labels. It is a "column" matrix of size TotalImagePixels x 1.
Centers - This what you refer to as "Colors". This is a float matrix that contains the cluster centers. The matrix is of size
NumberOfClusters x featureMean.
In this case, as you are using BGR pixels as "features" consider that Centers has 3 columns: One mean for the B channel, one mean for the G channel and finally, a mean for the R channel.
So, basically you loop through the (plain) label matrix, retrieve the label, use this value as index in the Centers matrix to retrieve the 3 colors.
One way to do this is as follows, using the auto data specifier and looping through the input image instead (that way we can index each input label easier):
//prepare an empty output matrix
cv::Mat outputImage( inputImage.size(), inputImage.type() );
//loop thru the input image rows...
for( int row = 0; row != inputImage.rows; ++row ){
//obtain a pointer to the beginning of the row
//alt: uchar* outputImageBegin = outputImage.ptr<uchar>(row);
auto outputImageBegin = outputImage.ptr<uchar>(row);
//obtain a pointer to the end of the row
auto outputImageEnd = outputImageBegin + outputImage.cols * 3;
//obtain a pointer to the label:
auto labels_ptr = labels.ptr<int>(row * inputImage.cols);
//while the end of the image hasn't been reached...
while( outputImageBegin != outputImageEnd ){
//current label index:
int const cluster_idx = *labels_ptr;
//get the center of that index:
auto centers_ptr = centers.ptr<float>(cluster_idx);
//we got an implicit VEC3B vector, we must map the BGR items to the
//output mat:
clusteredImageBegin[0] = centers_ptr[0];
clusteredImageBegin[1] = centers_ptr[1];
clusteredImageBegin[2] = centers_ptr[2];
//increase the row "iterator" of our matrices:
clusteredImageBegin += 3; ++labels_ptr;
}
}

Summing BGR in each column of ROI? openCV C++

I am trying to sum together the BGR values of the entire column of a ROI of a camera image. Right now, I loop through the matrix, and assign each BGR value to a variable to put into my 2d vector. I am sure I am doing this wrong, and that there is a much better way.
Vec3b rgbVal;
for (int r = 0; r < img1.rows; ++r) {
vector <double> colVal;
for (int c = 0; c < img1.cols; ++c) {
rgbVal = img1.at<Vec3b>(r, c); //Converts Mat image to Vec3b for BRG access
//Accesses the BRG values of each pixel in the column of the ROI, and adds it to values
values += static_cast<double>(rgbVal[0]) + static_cast<double>(rgbVal[1]) + static_cast<double>(rgbVal[2]);
colVal.push_back(values);
}
j.push_back(colVal);
values = 0;
}
So in my ROI, I am trying to sum together ALL the BGR values of every pixel in each column. Can someone help me out with this and point me in the right direction?
Thanks!

How to store point co-ordinates in vector data type in opencv?

I have a Mat object (CV_8UC1) which is a binary map with 1's on some positions and zeros otherwise.I want to create a vector which stores the co-ordinates of the points where the Mat is 1.Any suggestions on how to go about it.
I know that I can loop around the image and check points with the following code
for( int p = 1; p <= img.rows; p++ )
{ for( int q = 1; q <= img.cols; q++ )
{
if( img.at<uchar>(p,q) == 1 )
{
//what to do here ?
}
}
}
Also, I need the co-ordinates to be single precision floating point numbers.This is to use it as an input for another function which requires vectors.
Please gimme a hint.I am not familiar with vector data types and STL.
If you want to find all non-zero coordinates in a binary map with OpenCV, th ebest is to use findNonZero.
Here is an example of how to use it (with a dummy matrix but you get the idea):
cv::Mat img(100, 100, CV_8U, cv::Scalar(0));
img.at<uchar>(50, 50) = 255;
img.at<uchar>(70, 50) = 255;
img.at<uchar>(58, 30) = 255;
cv::Mat nonZeroes;
cv::findNonZero(img, nonZeroes);
std::vector<cv::Point2f> coords(nonZeroes.total());
for (int i = 0; i < nonZeroes.total(); i++) {
coords[i] = nonZeroes.at<cv::Point>(i);
}

outputting image back to matlab Mex

I am trying to output an image from my mex file back to my matlab file, but when i open it in matlab it is not correct.
The output image withing the mex file is correct
I have tried switching the orientation of the mwSize as well as swapping i and j in new_img.at<int>(j, i);
Mat image = imread(mxArrayToString(prhs[0]));
Mat new_img(H,W, image.type(), Scalar(0));
// some operations on new_img
imshow( "gmm image", image ); //shows the original image
imshow( "gmm1 image", new_img ); //shows the output image
waitKey( 200 ); //both images are the same size as desired
mwSize nd = 2;
mwSize dims[] = {W, H};
plhs[0] = mxCreateNumericArray(nd, dims, mxUINT8_CLASS, mxREAL);
if(plhs == NULL) {
mexErrMsgTxt("Could not create mxArray.\n");
}
char* outMat = (char*) mxGetData( plhs[0]);
for (int i= 0; i < H; i++)
{
for (int j = 0; j < W; j++)
{
outMat[i +j*image.rows] = new_img.at<int>(j, i);
}
}
this is in the mat file
gmmMask = GmmMex2(imgName,rect);
imshow(gmmMask); % not the same as the output image. somewhat resembles it, but not correct.
Because you have alluded to this being a colour image, this means that you have three slices of the matrix to consider. Your code only considers one slice. First off you need to make sure that you declare the right size of the image. In MATLAB, the first dimension is always the number of rows while the second dimension is the number of columns. Now you have to add the number of channels too on top of this. I'm assuming this is an RGB image so there are three channels.
Therefore, change your dims to:
mwSize nd = 3;
mwSize dims[] = {H, W, nd};
Changing nd to 3 is important as this will allow you to create a 3D matrix. You only have a 2D matrix. Next, make sure that you are accessing the image pixels at the right location in the cv::Mat object. The way you are accessing the image pixels in the nested pair of for loops assumes a row-major fashion (iterating over the columns first, then the rows). As such, you need to interchange i and j as i accesses the rows and j accesses the columns. You will also need to access the channel of the colour image so you'll need another for loop to compensate. For the grayscale case, you have properly compensated for the column-major memory configuration for the MATLAB MEX matrix though. This is verified because j accesses the columns and you need to skip over by rows amount in order to access the next column. However, to accommodate for a colour image, you must also skip over by image.rows*image.cols to go to the next layer of pixels.
Therefore your for loop should now be:
for (int k = 0; k < nd; k++) {
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
outMat[k*image.rows*image.cols + i + j*image.rows] = new_img.at<uchar>(i, j, k);
}
}
}
Take note that the container of pixels is most likely 8-bit unsigned character, and so you must change the template to uchar not int. This may also explain why your program is crashing.

Get coordinates of white pixels (OpenCV)

In OpenCV (C++) I have a b&w image where some shapes appear filled with white (255). Knowing this, how can I get the coordinate points in the image where theses objects are? I'm interested on getting all the white pixels coordinates.
Is there a cleaner way than this?
std::vector<int> coordinates_white; // will temporaly store the coordinates where "white" is found
for (int i = 0; i<img_size.height; i++) {
for (int j = 0; j<img_size.width; j++) {
if (img_tmp.at<int>(i,j)>250) {
coordinates_white.push_back(i);
coordinates_white.push_back(j);
}
}
}
// copy the coordinates into a matrix where each row represents a *(x,y)* pair
cv::Mat coordinates = cv::Mat(coordinates_white.size()/2,2,CV_32S,&coordinates_white.front());
there is a built-in function to do that cv::findNonZero
Returns the list of locations of non-zero pixels.
Given a binary matrix (likely returned from an operation such as cv::threshold(), cv::compare(), >, ==, etc) returns all of the non-zero indices as a cv::Mat or std::vector<cv::Point>
For example:
cv::Mat binaryImage; // input, binary image
cv::Mat locations; // output, locations of non-zero pixels
cv::findNonZero(binaryImage, locations);
// access pixel coordinates
Point pnt = locations.at<Point>(i);
or
cv::Mat binaryImage; // input, binary image
vector<Point> locations; // output, locations of non-zero pixels
cv::findNonZero(binaryImage, locations);
// access pixel coordinates
Point pnt = locations[i];
you can use this method to get the white pixels.. hope it will help u.
for(int i = 0 ;i <image.rows() ; i++){// image:the binary image
for(int j = 0; j< image.cols() ; j++){
double[] returned = image.get(i,j);
int value = (int) returned[0];
if(value==255){
System.out.println("x: " +i + "\ty: "+j);//(x,y) coordinates
}
}
}