Better ways to create a rectangular mask by openCV - c++

Creating a mask in openCV
/** result I want
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
*/
cv::Mat mask = cv::Mat::zeros(8, 8, CV_8U);
std::cout<<"before : \n"<<mask<<std::endl;
for(int i = 2; i != 6; ++i)
{
auto ptr = mask.ptr<uchar>(i) + 2;
for(int j = 0; j != 4; ++j)
{
*ptr++ = 1;
}
}
std::cout<<"after : \n"<<mask<<std::endl;
Do openCV provide us any build in function to create a mask like this?
It is trivial to create a function fot this task, but the function of openCV
always faster than naive handcrafted codes

sure, there's an easier way, use the roi operator:
cv::Mat mask = cv::Mat::zeros(8, 8, CV_8U); // all 0
mask(Rect(2,2,4,4)) = 1;
done!

If some one is looking for creating a non rectangular mask and then to apply it on the image then have a look here :
Mat& obtainIregularROI(Mat& origImag, Point2f topLeft, Point2f topRight, Point2f botLeft, Point2f botRight){
static Mat black(origImag.rows, origImag.cols, origImag.type(), cv::Scalar::all(0));
Mat mask(origImag.rows, origImag.cols, CV_8UC1, cv::Scalar(0));
vector< vector<Point> > co_ordinates;
co_ordinates.push_back(vector<Point>());
co_ordinates[0].push_back(topLeft);
co_ordinates[0].push_back(botLeft);
co_ordinates[0].push_back(botRight);
co_ordinates[0].push_back(topRight);
drawContours( mask,co_ordinates,0, Scalar(255),CV_FILLED, 8 );
origImag.copyTo(black,mask);
return black;
}
"black" is the image where we will finally obtain the result by cropping out the irregular ROI from the original image.
static Mat black(origImag.rows, origImag.cols, origImag.type(), cv::Scalar::all(0));
The "mask" is a Mat, initialized as the same size of original image and filled with 0.
Mat mask(origImag.rows, origImag.cols, CV_8UC1, cv::Scalar(0));
Putting the coordinates in ANTICLOCKWISE direction
vector< vector<Point> > co_ordinates;
co_ordinates.push_back(vector<Point>());
co_ordinates[0].push_back(topLeft);
co_ordinates[0].push_back(botLeft);
co_ordinates[0].push_back(botRight);
co_ordinates[0].push_back(topRight);
Now generating the mask actually
drawContours( mask,co_ordinates,0, Scalar(255),CV_FILLED, 8 );
At the end copy the masked portion/ROI from original image (origImag) and paste on the portion of ROI from the original image (using mask) into image named as "black"
origImag.copyTo(black,mask);

Related

Image rotation by very small step size in openCV c++

I'm trying to rotate an image by small degrees like 1 degree or below 1 degree,
Consider this is my Source Image and I'm trying to rotate it by 1 degree in MATLAB and OpenCV (C++) :
0 0 0 0
0 255 255 0
0 255 255 0
0 0 0 0
When I rotate it in MATLAB by 1 degree this is the result:
0 0 2.223835069639144 0
2.223835069639553 252.7942370468069 2.527942370468065 0
0 252.7942370468065 252.794237046807 2.223835069639648
0 2.223835069639184 0 0
This is the code in MATLAB:
sourceImage = [0 0 0 0; 0 255.0 255.0 0; 0 255.0 255.0 0; 0 0 0 0];
rotationDegree = 1.0;
shearInX_Y1 = (cosd(rotationDegree)-1)/sind(rotationDegree);
shearInX_Y2 = sind(rotationDegree);
transformationMatrix = [(1 + shearInX_Y1*shearInX_Y2), (2*shearInX_Y1 + ...
((shearInX_Y1)^2)*shearInX_Y2), 0; (shearInX_Y2), (1 + shearInX_Y1*shearInX_Y2), 0; 0, 0, 1];
tform = affine2d(transformationMatrix);
imref = imref2d(size(sourceImage));
imref.XWorldLimits = imref.XWorldLimits-mean(imref.XWorldLimits);
imref.YWorldLimits = imref.YWorldLimits-mean(imref.YWorldLimits);
transformedImg2 = imwarp(sourceImage , imref, tform, 'OutputView', imref, 'Interp', 'bilinear');
transformedImg2
The transformationMatrix in Matlab (which is our rotation matrix) is:
transformationMatrix =
9.998476951563913e-01 -1.745240643728003e-02 0
1.745240643728351e-02 9.998476951563913e-01 0
0 0 1.000000000000000e+00
And the content of "tform.T" is:
9.998476951563913e-01 -1.745240643728003e-02 0
1.745240643728351e-02 9.998476951563913e-01 0
0 0 1
And the content of "rotationMatrix" in OpenCV is:
9.998476951563913e-01 1.745240643728351e-02 -0.008650050796837391
-1.745240643728351e-02 9.998476951563913e-01 0.008802355640446121
But when I do the rotation by 1 degree in OpenCV (C++) This is the result:
(which is same as source image!) which means openCV has a problem with small rotations!
0 0 0 0
0 255 255 0
0 255 255 0
0 0 0 0
This is the code I use for rotation in OpenCV (C++): (Rotation is done with respect to image center)
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
# define INTERPOLATION_METHOD INTER_CUBIC
// INTER_AREA
// INTER_LINEAR
// INTER_NEAREST
Mat rotateImage(Mat sourceImage, double rotationDegree);
int main(){
Mat sourceImage = Mat::zeros(4, 4, CV_64F);
sourceImage.at<double>(1, 1) = 255.0;
sourceImage.at<double>(1, 2) = 255.0;
sourceImage.at<double>(2, 1) = 255.0;
sourceImage.at<double>(2, 2) = 255.0;
double rotationDegree = 1.0;
Mat rotatedImage = rotateImage(sourceImage, rotationDegree);
cout << "sourceImage: \n" << sourceImage << endl << endl;
cout << "rotatedImage : \n" << rotatedImage << endl << endl;
return 0;
}
Mat rotateImage(Mat sourceImage, double rotationDegree){
double rowOfImgCenter;
double colOfImgCenter;
rowOfImgCenter = sourceImage.rows / 2.0 - 0.5;
colOfImgCenter = sourceImage.cols / 2.0 - 0.5;
Point2d imageCenter(colOfImgCenter, rowOfImgCenter);
Mat rotationMatrix;
rotationMatrix = getRotationMatrix2D(imageCenter, rotationDegree, 1.0);
Mat rotatedImage;
warpAffine(sourceImage, rotatedImage, rotationMatrix, sourceImage.size(), INTERPOLATION_METHOD);
return rotatedImage;
}
Any idea would be appreciated.

How to implement the imrotate functioin using OpenCV

Yes. I want to get the same result like the imrotate in matlab, but I can't get the point.
More specifically,I get a mat A
a =
1 2 3
4 5 6
imrotate(a, 45)
ans =
0 0 0 0 0
0 0 3 6 0
0 1 2 6 0
0 1 4 0 0
0 0 0 0 0
How can I do that using OpenCV?
You may need use getRotationMatrix2D and warpAffine.
like this:
Mat A(5, 5, CV_8U, Scalar(0));
Point center(3, 3);
double angle = 45;
int value=1;
for (int i = 2 ; i<4 ;i++)
{
for (int j = 1 ; j <4 ;j++)
{
A.at(j,i) = value;
value++;
}
}
Mat matrix = getRotationMatrix2D(center, angle, 1.0);
Mat imgRotation;
warpAffine(A, imgRotation, matrix, Size(5, 5));

How to create circular mask for Mat object in OpenCV / C++?

My goal is to create a circular mask on a Mat object, so e.g. for a Mat looking like this:
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
...modify it such that I obtain a "circular shape" of 1s within it, so .e.g.
0 0 0 0 0
0 0 1 0 0
0 1 1 1 0
0 0 1 0 0
0 0 0 0 0
I am currently using the following code:
typedef struct {
double radius;
Point center;
} Circle;
...
for (Circle c : circles) {
// get the circle's bounding rect
Rect boundingRect(c.center.x-c.radius, c.center.y-c.radius, c.radius*2,c.radius*2);
// obtain the image ROI:
Mat circleROI(stainMask_, boundingRect);
int radius = floor(radius);
circle(circleROI, c.center, radius, Scalar::all(1), 0);
}
The problem is that after my call to circle, there is at most only one field in the circleROI set to 1... According to my understanding, this code should work because circle is supposed to use the information about the center and the radius to modify circleROI such that all points that are within the area of the circle should be set to 1... does anyone have an explanation for me what I am doing wrong? Am I taking the right approach to the problem but the actual issue might be somewhere else (this is very much possible too, since I am a novice to C++ and OpenCv)?
Note that I also tried to modify the last parameter in the circle call (which is the thickness of the circle outline) to 1 and -1, without any effect.
It is because you're filling your circleROI with a coordinate of the circle in the big mat. Your circle coordinate inside the circleROI should be relative to the circleROI, which is, in your case: new_center = (c.radius, c.radius), new_radius = c.radius.
Here is a snipcode for the loop:
for (Circle c : circles) {
// get the circle's bounding rect
Rect boundingRect(c.center.x-c.radius, c.center.y-c.radius, c.radius*2+1,c.radius*2+1);
// obtain the image ROI:
Mat circleROI(stainMask_, boundingRect);
//draw the circle
circle(circleROI, Point(c.radius, c.radius), c.radius, Scalar::all(1), -1);
}
Take a look at: getStructuringElement
http://docs.opencv.org/modules/imgproc/doc/filtering.html

OpenCV - get grey value from an image

Hello everybody right now I'm trying to getting grey value for every pixel in an image
what I mean with grey value is the white or black level from an image let's say 0 for white and 1 for black. for an example for this image
the value I want will be like
0 0 0 0 0 0
0 1 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 0 0 0 0
is this possible? if yes how to do it with OpenCV in C? or if it's impossible with OpenCV is there any other library that can do this?
What you ask is certainly possible but how it can be done depends on a lot of things. If you use C++, on SO we generally expect you to use the C++ interface which means you have a cv::Mat object and loaded the image with something like this: (using namespace cv)
#include <opencv2/core/core.hpp>
Mat mat_gray = imread(path, CV_LOAD_IMAGE_GRAYSCALE);
or by
Mat mat = imread(path); // and assuming it was originally a color image...
Mat mat_gray;
cvtColor(mat, mat_gray, CV_BGR2GRAY); //...convert it to grayscale.
Now, if you just want to access pixel values one-by-one, you use _Tp& mat.at<_Tp>(int x,int y);. That is:
for(int x=0; x<mat_gray.rows; ++x)
for(int y=0; y<mat_gray.cols; ++y)
mat_gray.at<uchar>(x,y); // if mat.type == CV_8U
You can look up your type here, which you should use in place of uchar if the mat.type is other than CV_8U.
As for the pure C interface, you can check this answer. But if you use C++, you should definitely use the C++ interface.

implementing erosion, dilation in C, C++

I have theoretical understanding of how dilation in binary image is done.
AFAIK, If my SE (structuring element) is this
0 1
1 1.
where . represents the centre, and my image(binary is this)
0 0 0 0 0
0 1 1 0 0
0 1 0 0 0
0 1 0 0 0
0 0 0 0 0
so the result of dilation is
0 1 1 0 0
1 1 1 0 0
1 1 0 0 0
1 1 0 0 0
0 0 0 0 0
I got above result by shifting Image in 0, +1 (up) and and -1(left) direction, according to SE, and taking the union of all these three shifts.
Now, I need to figure out how to implement this in C, C++.
I am not sure how to begin and how to take the union of sets.
I thought of representing original image,three shifted images and final image obtained by taking union; all using matrix.
Is there any place where I can get some sample solution to start with or any ideas to proceed ?
Thanks.
There are tons of sample implementations out there.. Google is your friend :)
EDIT
The following is a pseudo-code of the process (very similar to doing a convolution in 2D). Im sure there are more clever way to doing it:
// grayscale image, binary mask
void morph(inImage, outImage, kernel, type) {
// half size of the kernel, kernel size is n*n (easier if n is odd)
sz = (kernel.n - 1 ) / 2;
for X in inImage.rows {
for Y in inImage.cols {
if ( isOnBoundary(X,Y, inImage, sz) ) {
// check if pixel (X,Y) for boundary cases and deal with it (copy pixel as is)
// must consider half size of the kernel
val = inImage(X,Y); // quick fix
}
else {
list = [];
// get the neighborhood of this pixel (X,Y)
for I in kernel.n {
for J in kernel.n {
if ( kernel(I,J) == 1 ) {
list.add( inImage(X+I-sz, Y+J-sz) );
}
}
}
if type == dilation {
// dilation: set to one if any 1 is present, zero otherwise
val = max(list);
} else if type == erosion {
// erosion: set to zero if any 0 is present, one otherwise
val = min(list);
}
}
// set output image pixel
outImage(X,Y) = val;
}
}
}
The above code is based on this tutorial (check the source code at the end of the page).
EDIT2:
list.add( inImage(X+I-sz, Y+J-sz) );
The idea is that we want to superimpose the kernel mask (of size nxn) centered at sz (half size of mask) on the current image pixel located at (X,Y), and then just get the intensities of the pixels where the mask value is one (we are adding them to a list). Once extracted all the neighbors for that pixel, we set the output image pixel to the maximum of that list (max intensity) for dilation, and min for erosion (of course this only work for grayscale images and binary mask)
The indices of both X/Y and I/J in the statement above are assumed to start from 0.
If you prefer, you can always rewrite the indices of I/J in terms of half the size of the mask (from -sz to +sz) with a small change (the way the tutorial I linked to is using)...
Example:
Consider this 3x3 kernel mask placed and centered on pixel (X,Y), and see how we traverse the neighborhood around it:
--------------------
| | | | sz = 1;
-------------------- for (I=0 ; I<3 ; ++I)
| | (X,Y) | | for (J=0 ; J<3 ; ++J)
-------------------- vect.push_back( inImage.getPixel(X+I-sz, Y+J-sz) );
| | | |
--------------------
Perhaps a better way to look at it is how to produce an output pixel of the dilation. For the corresponding pixel in the image, align the structuring element such that the origin of the structuring element is at that image pixel. If there is any overlap, set the dilation output pixel at that location to 1, otherwise set it to 0.
So this can be done by simply looping over each pixel in the image and testing whether or not the properly shifted structuring element overlaps with the image. This means you'll probably have 4 nested loops: x img, y img, x se, y se. So for each image pixel, you loop over the pixels of the structuring element and see if there is any overlap. This may not be the most efficient algorithm, but it is probably the most straightforward.
Also, I think your example is incorrect. The dilation depends on the origin of the structuring element. If the origin is...
at the top left zero: you need to shift the image (-1,-1), (-1,0), and (0,-1) giving:
1 1 1 0 0
1 1 0 0 0
1 1 0 0 0
1 0 0 0 0
0 0 0 0 0
at the bottom right: you need to shift the image (0,0), (1,0), and (0,1) giving:
0 0 0 0 0
0 1 1 1 0
0 1 1 0 0
0 1 1 0 0
0 1 0 0 0
MATLAB uses floor((size(SE)+1)/2) as the origin of the SE so in this case, it will use the top left pixel of the SE. You can verify this using the imdilate MATLAB function.
OpenCV
Example: Erosion and Dilation
/* structure of the image variable
* variable n stores the order of the square matrix */
typedef struct image{
int mat[][];
int n;
}image;
/* function recieves image "to dilate" and returns "dilated"*
* structuring element predefined:
* 0 1 0
* 1 1 1
* 0 1 0
*/
image* dilate(image* to_dilate)
{
int i,j;
int does_order_increase;
image* dilated;
dilated = (image*)malloc(sizeof(image));
does_order_increase = 0;
/* checking whether there are any 1's on d border*/
for( i = 0 ; i<to_dilate->n ; i++ )
{
if( (to_dilate->a[0][i] == 1)||(to_dilate->a[i][0] == 1)||(to_dilate->a[n-1][i] == 1)||(to_dilate->a[i][n-1] == 1) )
{
does_order_increase = 1;
break;
}
}
/* size of dilated image initialized */
if( does_order_increase == 1)
dilated->n = to_dilate->n + 1;
else
dilated->n = to_dilate->n;
/* dilating image by checking every element of to_dilate and filling dilated *
* does_order_increase serves to cope with adjustments if dilated 's order increase */
for( i = 0 ; i<to_dilate->n ; i++ )
{
for( j = 0 ; j<to_dilate->n ; j++ )
{
if( to_dilate->a[i][j] == 1)
{
dilated->a[i + does_order_increase][j + does_order_increase] = 1;
dilated->a[i + does_order_increase -1][j + does_order_increase ] = 1;
dilated->a[i + does_order_increase ][j + does_order_increase -1] = 1;
dilated->a[i + does_order_increase +1][j + does_order_increase ] = 1;
dilated->a[i + does_order_increase ][j + does_order_increase +1] = 1;
}
}
}
/* dilated stores dilated binary image */
return dilated;
}
/* end of dilation */