Change pixel data of a rectangle in opencv - c++

I used the rectangle array below to loop faces detected by Haar Classifier:
for( int i = 0; i < (objects ? objects->total : 0 ); i++ )
{
CvRect* r = ( CvRect* )cvGetSeqElem( objects, i );
cvRectangle( frame, cvPoint( r->x, r->y ), cvPoint( r->x + r->width, r->y + r->height ),colors[i%1]);
}
But I want to change the pixel data of each face detected in the classifier i.e. change the values of pixels of each rectangle in:
CvRect* r;
I tried:
for( int i = 0; i < (objects ? objects->total : 0 ); i++ )
{
r[i];
for(int j = r->y; j < r->y + r->height; j++)
{
for(int k = r->x; k < r->x + r->width; k++)
{
frame->imageData[k*3] = 0;
frame->imageData[k*3+2] = 0;
}
}
}
to keep only G channel of the face but it is saying that the variable 'r' is not declared.

In this loop:
for( int i = 0; i < (objects ? objects->total : 0 ); i++ )
{
CvRect* r = ( CvRect* )cvGetSeqElem( objects, i );
cvRectangle( frame, cvPoint( r->x, r->y ), ... );
}
properly initialized temporary local variable r is being used, while in this code:
CvRect* r;
for( int i = 0; i < (objects ? objects->total : 0 ); i++ )
{
r[i];
for(int j = r->y; j < r->y + r->height; j++)
{
for(int k = r->x; k < r->x + r->width; k++)
{
frame->imageData[k*3] = 0;
frame->imageData[k*3+2] = 0;
}
}
}
uninitialized pointer r is treated as an array and even within the nonsense expression making this code invalid.
Try to replace r[i] with r = ( CvRect* )cvGetSeqElem( objects, i );

Use a new image instance as a roi pointer. Example:
Mat myimage(500,500,CV_8U,Scalar(255));
imshow("image",myimage); //white image
cvWaitKey();
//Reference matrix
Mat roi_img(myimage(cvRect(25,25,100,100)));
roi_img.setTo(Scalar(0));
imshow("image",myimage); //image has a black rect area.
cvWaitKey();

Related

What should I change in this function for grayscale images to work

I have this C++ function to create an image from multiple images:
Mat imageCollage(vector<Mat> & array_of_images, int M, int N )
{
// All images should be the same size
const Size images_size = array_of_images[0].size();
// Create a black canvas
Mat image_collage( images_size.height * M, images_size.width * N, CV_8UC3, Scalar( 0, 0, 0 ) );
for( int i = 0; i < M; ++i )
{
for( int j = 0; j < N; ++j )
{
if( ( ( i * N ) + j ) >= array_of_images.size() )
break;
Rect roi( images_size.width * j, images_size.height * i, images_size.width, images_size.height );
array_of_images[ ( i * N ) + j ].copyTo( image_collage( roi ) );
}
}
return image_collage;
}
My program is supposed to create an image collage from multiple images and it works for RGB images but not when I convert them to grayscale. I tested separately the functions and I think it's this one that creates the problem. This is the error I get:
OpenCV(4.5.0-dev) /home/csimage/Documents/opencv-repos/opencv/modules/core/src/copy.cpp:254: error: (-215:Assertion failed) channels() == CV_MAT_CN(dtype) in function 'copyTo'
Aborted (core dumped)

from float array to mat , concatenate blocks of image

I have an image 800x800 which is broken down to 16 blocks of 200x200.
(you can see previous post here)
These blocks are : vector<Mat> subImages;
I want to use float pointers on them , so I am doing :
float *pdata = (float*)( subImages[ idxSubImage ].data );
1) Now, I want to be able to get again the same images/blocks, going from float array to Mat data.
int Idx = 0;
pdata = (float*)( subImages[ Idx ].data );
namedWindow( "Display window", WINDOW_AUTOSIZE );
for( int i = 0; i < OriginalImgSize.height - 4; i+= 200 )
{
for( int j = 0; j < OriginalImgSize.width - 4; j+= 200, Idx++ )
{
Mat mf( i,j, CV_32F, pdata + 200 );
imshow( "Display window", mf );
waitKey(0);
}
}
So , the problem is that I am receiving an
OpenCV Error: Assertion failed
in imshow.
2) How can I recombine all the blocks to obtain the original 800x800 image?
I tried something like:
int Idx = 0;
pdata = (float*)( subImages[ Idx ].data );
Mat big( 800,800,CV_32F );
for( int i = 0; i < OriginalImgSize.height - 4; i+= 200 )
{
for( int j = 0; j < OriginalImgSize.width - 4; j+= 200, Idx++ )
{
Mat mf( i,j, CV_32F, pdata + 200 );
Rect roi(j,i,200,200);
mf.copyTo( big(roi) );
}
}
imwrite( "testing" , big );
This gives me :
OpenCV Error: Assertion failed (!fixedSize()) in release
in mf.copyTo( big(roi) );.
First, you need to know where are your subimages into the big image. To do this, you can save the rect of each subimage into the vector<Rect> smallImageRois;
Then you can use pointers (keep in mind that subimages are not continuous), or simply use copyTo to the correct place:
Have a look:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
resize(img, img, Size(800, 800));
Mat grayImg;
cvtColor(img, grayImg, COLOR_BGR2GRAY);
grayImg.convertTo(grayImg, CV_32F);
int N = 4;
if (((grayImg.rows % N) != 0) || ((grayImg.cols % N) != 0))
{
// Error
return -1;
}
Size graySize = grayImg.size();
Size smallSize(grayImg.cols / N, grayImg.rows / N);
vector<Mat> smallImages;
vector<Rect> smallImageRois;
for (int i = 0; i < graySize.height; i += smallSize.height)
{
for (int j = 0; j < graySize.width; j += smallSize.width)
{
Rect rect = Rect(j, i, smallSize.width, smallSize.height);
smallImages.push_back(grayImg(rect));
smallImageRois.push_back(rect);
}
}
// Option 1. Using pointer to subimage data.
Mat big1(800, 800, CV_32F);
int big1step = big1.step1();
float* pbig1 = big1.ptr<float>(0);
for (int idx = 0; idx < smallImages.size(); ++idx)
{
float* pdata = (float*)smallImages[idx].data;
int step = smallImages[idx].step1();
Rect roi = smallImageRois[idx];
for (int i = 0; i < smallSize.height; ++i)
{
for (int j = 0; j < smallSize.width; ++j)
{
pbig1[(roi.y + i) * big1step + (roi.x + j)] = pdata[i * step + j];
}
}
}
// Option 2. USing copyTo
Mat big2(800, 800, CV_32F);
for (int idx = 0; idx < smallImages.size(); ++idx)
{
smallImages[idx].copyTo(big2(smallImageRois[idx]));
}
return 0;
}
For concatenating the sub-images into a single squared image, you can use the following function:
// Important: all patches should have exactly the same size
Mat concatPatches(vector<Mat> &patches) {
assert(patches.size() > 0);
// make it square
const int patch_width = patches[0].cols;
const int patch_height = patches[0].rows;
const int patch_stride = ceil(sqrt(patches.size()));
Mat image = Mat::zeros(patch_stride * patch_height, patch_stride * patch_width, patches[0].type());
for (size_t i = 0, iend = patches.size(); i < iend; i++) {
Mat &patch = patches[i];
const int offset_x = (i % patch_stride) * patch_width;
const int offset_y = (i / patch_stride) * patch_height;
// copy the patch to the output image
patch.copyTo(image(Rect(offset_x, offset_y, patch_width, patch_height)));
}
return image;
}
It takes a vector of sub-images (or patches as I refer them to) and concatenates them into a squared image. Example usage:
vector<Mat> patches;
vector<Scalar> colours = {Scalar(255, 0, 0), Scalar(0, 255, 0), Scalar(0, 0, 255)};
// fill vector with circles of different colours
for(int i = 0; i < 16; i++) {
Mat patch = Mat::zeros(100,100, CV_32FC3);
circle(patch, Point(50,50), 40, colours[i % 3], -1);
patches.push_back(patch);
}
Mat img = concatPatches(patches);
imshow("img", img);
waitKey();
Will produce the following image
print the values of i and j before creating Mat mf and I believe you will soon be able to find the error.
Hint 1: i and j will be 0 the first time
Hint 2: Use the copyTo() with a ROI like:
cv::Rect roi(0,0,200,200);
src.copyTo(dst(roi))
Edit:
Hint 3: Try not to do such pointer fiddling, you will get in trouble. Especially if you're ignoring the step (like you seem to do).

Three images are displayed in output window instead of one

Hi everyone i tried using kmeans clustering to group the objects. So that i can use this clustering method to detect objects. I get output but the problem is its too slow{How can i solve this?? } and i get the output window is as shown in the below link. Three output images are displayed instead of one how can i solve this. I don't know where exactly the error lies.
http://tinypic.com/view.php?pic=30bd7dc&s=8#.VgkSIPmqqko
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main( )
{
Mat src = imread( "Light.jpg", 0 );
// imshow("fff",src);
// cvtColor(src,src,COLOR_BGR2GRAY);
Mat dst;
// pyrDown(src,src,Size( src.cols/2, src.rows/2 ),4);
// src=dst;
resize(src,src,Size(128,128),0,0,1);
Mat samples(src.rows * src.cols, 3, CV_32F);
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
// for( int z = 0; z < 3; z++)
samples.at<float>(y + x*src.rows) = src.at<uchar>(y,x);
cout<<"aaa"<<endl;
int clusterCount = 15;
Mat labels;
int attempts = 2;
Mat centers;
cout<<"aaa"<<endl;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers );
Mat new_image( src.size(), src.type() );
cout<<"aaa"<<endl;
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
{
int cluster_idx = labels.at<int>(y + x*src.rows,0);
new_image.at<uchar>(y,x) = centers.at<float>(cluster_idx,0);
//new_image.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1);
// new_image.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2);
}
imshow( "clustered image", new_image );
waitKey( 0 );
}
In your initial code you have to change the intermedia Mat sample from 3 channels to 1 channel if you use grayscale images.
In addition, if you change the memory ordering, it might be faster (changed to (y*src.cols + x, 0) in both places):
int main( )
{
clock_t start = clock();
Mat src = imread( "Light.jpg", 0 );
Mat dst;
resize(src,src,Size(128,128),0,0,1);
Mat samples(src.rows * src.cols, 1, CV_32F);
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
samples.at<float>(y*src.cols + x, 0) = src.at<uchar>(y,x);
int clusterCount = 15;
Mat labels;
int attempts = 2;
Mat centers;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers );
Mat new_image( src.size(), src.type() );
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
{
int cluster_idx = labels.at<int>(y*src.cols + x,0);
new_image.at<uchar>(y,x) = centers.at<float>(cluster_idx,0);
}
imshow( "clustered image", new_image );
clock_t end = clock();
std::cout << "time: " << (end - start)/(float)CLOCKS_PER_SEC << std::endl;
waitKey( 0 );
}

OpenCV Error: Assertion Failed in MixChannels(..)

I'm attempting to convert a MATLAB .mat file to openCV MAT and then applying several masks to those files. I am building from cvmatio source code. I am receiving the following error:
OpenCV Error: Assertion failed (A.size == arrays[i0]->size) in init,
file
/home/derek/Documents/Libraries/opencv-3.0.0-beta/modules/core/src/matrix.cpp,
line 4279 terminate called after throwing an instance of
'cv::Exception' what():
/home/derek/Documents/Libraries/opencv-3.0.0-beta/modules/core/src/matrix.cpp:4279:
error: (-215) A.size == arrays[i0]->size in function init
Here is the source file I've written. It occurs at the line with MixChannels. Note that SrcImage is a 3 channel Mat. lower and upper are the threshold values in an array who's length is equal to the number of channels.
/*
* Mask.cpp
*
* Created on: Mar 16, 2015
* Author: derek
*/
#include <cv.h>
#include <highgui.h>
#include "imgcodecs.hpp"
#include "highgui.hpp"
#include "imgproc.hpp"
using namespace cv;
Mat Mask(Mat SrcImage, double lower[], double upper[]){
int height=SrcImage.rows;
int width=SrcImage.cols;
int depth=SrcImage.depth();
Mat B2d = Mat::ones(height, width,depth);
Mat out(height, width, depth);
Mat outL(height, width, depth);
Mat outU(height,width, depth);
for (int i=1; i< SrcImage.channels(); i=i+1){
int from_to[]={i,1};
mixChannels(&SrcImage, 3, &out, 1, from_to, 1 );
threshold(out, outL, lower[i], 1, THRESH_BINARY);
threshold(out, outU, upper[i], 1, THRESH_BINARY);
bitwise_and(B2d, outL, B2d);
bitwise_and(B2d, outU, B2d);
}
return B2d;
}
Also, here is an excerpt of the actual CV_Assertion error location. As indicated in the error, it occurs at "(A.size == arrays[i0]->size)".
void NAryMatIterator::init(const Mat** _arrays, Mat* _planes, uchar** _ptrs, int _narrays)
{
CV_Assert( _arrays && (_ptrs || _planes) );
int i, j, d1=0, i0 = -1, d = -1;
arrays = _arrays;
ptrs = _ptrs;
planes = _planes;
narrays = _narrays;
nplanes = 0;
size = 0;
if( narrays < 0 )
{
for( i = 0; _arrays[i] != 0; i++ )
;
narrays = i;
CV_Assert(narrays <= 1000);
}
iterdepth = 0;
for( i = 0; i < narrays; i++ )
{
CV_Assert(arrays[i] != 0);
const Mat& A = *arrays[i];
if( ptrs )
ptrs[i] = A.data;
if( !A.data )
continue;
if( i0 < 0 )
{
i0 = i;
d = A.dims;
// find the first dimensionality which is different from 1;
// in any of the arrays the first "d1" step do not affect the continuity
for( d1 = 0; d1 < d; d1++ )
if( A.size[d1] > 1 )
break;
}
else
CV_Assert( A.size == arrays[i0]->size );
if( !A.isContinuous() )
{
CV_Assert( A.step[d-1] == A.elemSize() );
for( j = d-1; j > d1; j-- )
if( A.step[j]*A.size[j] < A.step[j-1] )
break;
iterdepth = std::max(iterdepth, j);
}
}
if( i0 >= 0 )
{
size = arrays[i0]->size[d-1];
for( j = d-1; j > iterdepth; j-- )
{
int64 total1 = (int64)size*arrays[i0]->size[j-1];
if( total1 != (int)total1 )
break;
size = (int)total1;
}
iterdepth = j;
if( iterdepth == d1 )
iterdepth = 0;
nplanes = 1;
for( j = iterdepth-1; j >= 0; j-- )
nplanes *= arrays[i0]->size[j];
}
else
iterdepth = 0;
idx = 0;
if( !planes )
return;
for( i = 0; i < narrays; i++ )
{
CV_Assert(arrays[i] != 0);
const Mat& A = *arrays[i];
if( !A.data )
{
planes[i] = Mat();
continue;
}
planes[i] = Mat(1, (int)size, A.type(), A.data);
}
}
Well it's obviously too late of an answer, but here is the reason why your code failed:
Since your SrcImage is a single Mat with multiple channels, you should'we written:
mixChannels(&SrcImage, 1, &out, 1, from_to, 1 );
(The assert error was related to this, since mixChannels expected 3 Mats, which is 3 times bigger than your Mat.)
Also opencv MixChannels labels channels from 0, not sure if the i=1 was intended, or just a typo.
Cheers!

cv::Erode error with binary cv::mat

So I'm trying to erode a binary matrix.
I create the matrix using this code:
cv::Mat tmp = cv::Mat::zeros( IMG->width, IMG->height, CV_8U );
for( auto i = 0 ; i < IMG->width ; i++)
{
for ( auto j = 0 ; j < IMG->height ; j++)
{
if( cv::pointPolygonTest(cv::Mat(contour),cv::Point(i,j),true) < 0 )
{
tmp.at<double>(i,j) = 255;
}
}
}
Here is the source picture I'm using:
And this what I get with my loop (it's the tmp matrix):
So after I'm trying to erode the picture using this code:
int erosion_elem = 1;
int erosion_size = 8;
int erosion_type;
if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement( erosion_type,
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size, erosion_size ) );
/// Apply the erosion operation
erode( binary, erosion_dst, element );`
So it compiles well but I get an exception on this line:
erode( binary, erosion_dst, element );`
It says it's an unsupported data type.
Does anyone have an idea why do I get this exception?
I tried to change the data type of the matrix tmp but I have the same error.
Thanks for your help !
Your binary image pixels are stored as unsigned char (CV_8U -> on 8bits -> 1 byte),
you should store your pixels' value as unsigned char too
cv::Mat tmp = cv::Mat::zeros( IMG->width, IMG->height, CV_8U );
for( auto i = 0 ; i < IMG->width ; i++)
{
for ( auto j = 0 ; j < IMG->height ; j++)
{
if( cv::pointPolygonTest(cv::Mat(contour),cv::Point(i,j),true) < 0 )
{
tmp.at<unsigned char>(i,j) = 255;
}
}
}
(made answer from comment)