Why does this rotate method give the image dead space? OpenCV - ios

I am using this method to rotate a cvMat, whenever I run it I get back a rotated image however there is a lot of deadspace below it.
void rotate(cv::Mat& src, double angle, cv::Mat& dst)
{
int len = std::max(src.cols, src.rows);
cv::Point2f pt(len/2., len/2.);
cv::Mat r = cv::getRotationMatrix2D(pt, angle, 1.0);
cv::warpAffine(src, dst, r, cv::Size(len, len));
}
When given this image:
I get this image:
The image has been rotated but as you can see some extra pixels have been added, how can I only rotate the original image and not add any extra pixels?
Method call:
rotate(src, skew, res);
res being dst.

As mayank-baddi said you have to use output image size same as the input to resolve this, and my answer is based on your comment above How can I avoid adding the black area? after wrapAffine,
So you have to do,
Create white image little bigger than your source, and it will depend on your skew angle, here I used 50 pixel.
int extend=50;
Mat tmp(src.rows+2*extend,src.cols+2*extend,src.type(),Scalar::all(255));
Copy the source to above using ROI
Rect ROI(extend,extend,src.cols,src.rows);
src.copyTo(tmp(ROI));
Now rotate tmp instead of src
rotate(tmp, skew, res); res being dst.
Crop back the final image from rotated result using the same ROI.
Mat crop=res(ROI);
imshow("crop",crop);

You have to define the output image size while using warpAffine transform.
Here you are defining the size as cv::Size(len, len) where len is max of height and width.
cv::warpAffine(src, dst, r, cv::Size(len, len));
Define/calculate the size of the final image accordingly.

Related

Extracting transparent background of an image with opencv

I have got a mask calculated in grab_cut(which calculates the foreground). I want to extract only the background leaving the foreground transparent. I manage to do so using the following code in order to extract foreground(background transparent). How is it possible to do the opposite?
int border = 20;
int border2 = border + border;
cv::Rect rectangle(border,border,image.cols-border2,image.rows-border2);
cv::Mat result; // segmentation result (4 possible values)
cv::Mat bgModel,fgModel; /
cv::grabCut(image, // input image
result, // segmentation result
rectangle,// rectangle containing foreground
bgModel,fgModel, // models
1, // number of iterations
cv::GC_INIT_WITH_RECT); // use rectangle
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(foreground,result); // bg pixels not copied
cv::rectangle(image, rectangle, cv::Scalar(255,255,255),1);
cv::imwrite(argv[2], foreground);
cv::imwrite(argv[3], image);
Mat dst;//(src.rows,src.cols,CV_8UC4);
Mat tmp,alpha;
cvtColor(foreground,tmp,CV_BGR2GRAY);
threshold(tmp,alpha,100,255,THRESH_BINARY);
Mat rgb[3];
split(foreground,rgb);
Mat rgba[4]={rgb[0],rgb[1],rgb[2],alpha};
merge(rgba,4,dst);
imwrite("dst.png",dst);
Basically i think I ve got to change those lines:
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(foreground,result); // bg pixels not copied
How is is possible to select the rest of the image the opposite of result?
Just invert your mask as in:
cv::Mat background(image.size(),CV_8UC3,cv::Scalar(255,255,255));
image.copyTo(background, ~result); // fg pixels not copied

OpenCV 2.4.2 Byte array to Mat produces a strange image pattern

Good afternoon,
I am trying to run OpenCV through a DLL and use it in a LabVIEW application.
I have correctly acquired an image in LV and passed the byte array to the DLL.
I can loop and print out in a text file the values for every pixel and match them to the output in LV, so I know that all my pixels are in the right position, for the exception that LV adds 2 columns at the beginning, with the first 2 values reserved for height and width and the rest are arbitrary numbers. But all this should do is produce a streak on the left side of the image.
Next, I am using the following lines to convert and display the image.
a[0], a[1]... etc. are channels.
The output image comes out as a very horizontally stretched out image with pixels spaced equally 15-20 pixels apart and surrounded by black pixels. I attached a screenshot
_declspec (dllexport) double imageProcess(int **a, int &x, int &y, int &cor,int &cog,int &cob,int &cow, int th, int hth)
{
y = a[0][0];
x = a[0][1];
Mat image(y, x, CV_8U, a[0]);
namedWindow( "Display window", CV_WINDOW_NORMAL ); // Create a window for display.
imshow( "Display window", image ); // Show our image inside it.
return (0);
}
Additionally I tried using this code with the same effect:
IplImage* cv_image = cvCreateImageHeader(cvSize(x,y), IPL_DEPTH_8U, 1);
cvSetData(cv_image, a[0], cv_image->widthStep);
Mat image = Mat(cv_image, false);
Can anyone please help me explain why this is happening during my image creation?
Note, Unfortunately, I cannot provide the original image/capture from LV, but I can say that it doesn't look anything like that and I am working with everything in grayscale.
Output Image:
your input ( a ) is a matrix of ints, while opencv wants uchars there.
the way you do it currently, each int (from a) gets spread over 4 consecutive bytes,
( that's exactly, what i see in that picture )
also it's only using the 1st 1/4 of the input data
you probably won't get away with just feeding the pixel pointer into your cv::Mat there,
looping over a[0], casting each pixel to uchar, and then assigning it to the opencv-pixel
should work, imho
You could convert your image to uchar or simple use an int image by replacing CV_8U by CV_32S and then:
int offset = 0;
int scale = 0;
cv::Mat image8U;
image.convertTo(image8U, CV_8UC1, scale, offset );

OpenCV Image processing

I have an Image that I would like to zoom into and view at high detail. It is of unknown size and mostly black and white with some text on it. When I zoom in the text becomes unreadable and I thought it was to do with not having enough pixels/texels to display so I upscaled the image by a factor of 2. Now that I have scaled it, it is still unreadable.
Then I started to use OpenCV with :
void resizeFirst(){
Mat src = imread( "../floor.png", 1 );
Mat tmp;
resize(src,tmp,Size(),3,3,INTER_CUBIC);
Mat dst = tmp.clone();
Size s(3,3);
//blur( tmp, dst, s, Point(-1,-1) );//homogeneous
GaussianBlur(tmp, dst, s, 3);//gaussian
//medianBlur ( tmp, dst, 5 );//median
//bilateralFilter ( tmp, dst, 5, 5*2, 5/2 );
addWeighted(tmp, 1.5, dst, -0.5, 0, dst);
imwrite("sharpenedImage.png",dst);
}
void blurFirst(){
Mat src = imread( "../floor.png", 1 );
Size s(3,3);
Mat dst;
GaussianBlur(src, dst, s, 3);//gaussian
addWeighted(src, 2, dst, -1, 0, dst);
Mat tmp;
resize(dst,tmp,Size(),3,3,INTER_CUBIC);
imwrite("sharpenedImage0.png",tmp);
}
and the output is better but the image still isnt sharp. Does anyone have any ideas on how to keep text sharp when zooming into an image?
EDIT: below are sample images.
The first one is the smaller res original and the second I resized and tried to do gaussian sharpening as per below
Resize function offers different interpolation methods
INTER_NEAREST nearest-neighbor interpolation
INTER_LINEAR bilinear interpolation (used by default)
INTER_AREA resampling using pixel area relation. It may be the preferred method for image decimation, as it gives moire-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method
INTER_CUBIC bicubic interpolation over 4x4 pixel neighborhood
INTER_LANCZOS4 Lanczos interpolation over 8x8 pixel neighborhood
try all the interpolation methods and use the one that suits you the most. The resize function will however change the aspect ratio of your image.

Crop and rotate picture OpenCV

I am new in OpenCV so please to be lenient.
I am doing an Android application to recognize the squares/rectangles and crop them. Function which looks for the squares/rectangles puts the found objects to vector> squares. I just wonder how to crop the picture according to the data in points stored in vector> squares and how to compute an angle on which the picture should be rotated. Thank you for any help
This post is citing from OpenCV QA: Extract a RotatedRect area.
There's a great article by Felix Abecassis on rotating and deskewing images. This also shows you how to extract the data in the RotatedRect:
http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/
You basically only need cv::getRotationMatrix2D to get the rotation matrix for the affine transformation with cv::warpAffine and cv::getRectSubPix to crop the rotated image. The relevant lines in my application are:
// This is the RotatedRect, I got it from a contour for example...
RotatedRect rect = ...;
// matrices we'll use
Mat M, rotated, cropped;
// get angle and size from the bounding box
float angle = rect.angle;
Size rect_size = rect.size;
// thanks to http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/
if (rect.angle < -45.) {
angle += 90.0;
swap(rect_size.width, rect_size.height);
}
// get the rotation matrix
M = getRotationMatrix2D(rect.center, angle, 1.0);
// perform the affine transformation on your image in src,
// the result is the rotated image in rotated. I am doing
// cubic interpolation here
warpAffine(src, rotated, M, src.size(), INTER_CUBIC);
// crop the resulting image, which is then given in cropped
getRectSubPix(rotated, rect_size, rect.center, cropped);
There are lots of useful posts around, I'm sure you can do a better search.
Crop:
cropping IplImage most effectively
Rotate:
OpenCV: how to rotate IplImage?
Rotating or Resizing an Image in OpenCV
Compute angle:
OpenCV - Bounding Box & Skew Angle
Altought this question is quite old, I think there is the need for an answer that is not expensive as rotating the whole image (see #bytefish's answer). You will need a bounding rect, for some reason rotatedRect.boundingRect() didn't work for me, so I had to use Imgproc.boundingRect(contour). This is OpenCV for Android, the operations are almost the same for other environments:
Rect roi = Imgproc.boundingRect(contour);
// we only work with a submat, not the whole image:
Mat mat = image.submat(roi);
RotatedRect rotatedRect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray()));
Mat rot = Imgproc.getRotationMatrix2D(rotatedRect.center, rotatedRect.angle, 1.0);
// rotate using the center of the roi
double[] rot_0_2 = rot.get(0, 2);
for (int i = 0; i < rot_0_2.length; i++) {
rot_0_2[i] += rotatedRect.size.width / 2 - rotatedRect.center.x;
}
rot.put(0, 2, rot_0_2);
double[] rot_1_2 = rot.get(1, 2);
for (int i = 0; i < rot_1_2.length; i++) {
rot_1_2[i] += rotatedRect.size.height / 2 - rotatedRect.center.y;
}
rot.put(1, 2, rot_1_2);
// final rotated and cropped image:
Mat rotated = new Mat();
Imgproc.warpAffine(mat, rotated, rot, rotatedRect.size);

color a grayscale image with opencv

i'm using openNI for some project with kinect sensor. i'd like to color the users pixels given with the depth map. now i have pixels that goes from white to black, but i want from red to black. i've tried with alpha blending, but my result is only that i have pixels from pink to black because i add (with addWeight) red+white = pink.
this is my actual code:
layers = device.getDepth().clone();
cvtColor(layers, layers, CV_GRAY2BGR);
Mat red = Mat(240,320, CV_8UC3, Scalar(255,0,0));
Mat red_body; // = Mat::zeros(240,320, CV_8UC3);
red.copyTo(red_body, device.getUserMask());
addWeighted(red_body, 0.8, layers, 0.5, 0.0, layers);
where device.getDepth() returns a cv::Mat with depth map and device.getUserMask() returns a cv::Mat with user pixels (only white pixels)
some advice?
EDIT:
one more thing:
thanks to sammy answer i've done it. but actually i don't have values exactly from 0 to 255, but from (for example) 123-220.
i'm going to find minimum and maximum via a simple for loop (are there better way?), and how can i map my values from min-max to 0-255 ?
First, OpenCV's default color format is BGR not RGB. So, your code for creating the red image should be
Mat red = Mat(240,320, CV_8UC3, Scalar(0,0,255));
For red to black color map, you can use element wise multiplication instead of alpha blending
Mat out = red_body.mul(layers, 1.0/255);
You can find the min and max values of a matrix M using
double minVal, maxVal;
minMaxLoc(M, &minVal, &maxVal, 0, 0);
You can then subtract the minValue and scale with a factor
double factor = 255.0/(maxVal - minVal);
M = factor*(M -minValue)
Kinda clumsy and slow, but maybe split layers, copy red_body (make it a one channel Mat, not 3) to the red channel, merge them back into layers?
Get the same effect, but much faster (in place) with reshape:
layers = device.getDepth().clone();
cvtColor(layers, layers, CV_GRAY2BGR);
Mat red = Mat(240,320, CV_8UC1, Scalar(255)); // One channel
Mat red_body;
red.copyTo(red_body, device.getUserMask());
Mat flatLayer = layers.reshape(1,240*320); // presumed dimensions of layer
red_body.reshape(0,240*320).copyTo(flatLayer.col(0));
// layers now has the red from red_body

Resources