I'm unsing cv::undistort but it crops the image. I'd like to have all the undistorted image, so that the undistorted size is bigger then the original one, like this:
I think I need to use cv::getOptimalNewCameraMatrix but I had no luck with my trials.. some help?
Just for the record:
You should use cv::getOptimalNewCameraMatrix and set the alpha parameter to 1. Alpha 0 only shows valid points on the image, alpha 1 shows all the original points as well as black regions. cv::getOptimalNewCameraMatrix aslo gives you a ROI to crop the result from cv::undistort.
This code would do the trick:
void loadUndistortedImage(std::string fileName, Mat & outputImage,
Mat & cameraMatrix, Mat & distCoeffs) {
Mat image = imread(fileName, CV_LOAD_IMAGE_GRAYSCALE);
// setup enlargement and offset for new image
double y_shift = 60;
double x_shift = 70;
Size imageSize = image.size();
imageSize.height += 2*y_shift;
imageSize.width += 2*x_shift;
// create a new camera matrix with the principal point
// offest according to the offset above
Mat newCameraMatrix = cameraMatrix.clone();
newCameraMatrix.at<double>(0, 2) += x_shift; //adjust c_x by x_shift
newCameraMatrix.at<double>(1, 2) += y_shift; //adjust c_y by y_shift
// create undistortion maps
Mat map1;
Mat map2;
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
newCameraMatrix, imageSize, CV_16SC2, map1, map2);
//remap
remap(image, outputImage, map1, map2, INTER_LINEAR);
}
See http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html
and http://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html
the best would be subclass OpenCV's class and overload the method undistort(), in order to access all the images you need.
Related
I already tried to search about openCV ROI function, but All of them used rectangle roi function.
I want to get roi using by inclined line that get from hough transform function.
My situation is next :
I have multiple vertical lines(little inclined) that output from hough transform function.
i want to get image(Matrix) between vertical lines.
enter image description here
i want to get divided matrix in my image (For example, A image, B image, C image etc.. )
Is there ROI function that used line in openCV?
or
any another method?
I think you need to use contours to define your roi. If it is not a perfect square you can not use the ROI function, because this is always a perfect square (not even a rotated square)
int main()
{
enum hierIdx { H_NEXT = 0, H_PREVIOUS, H_FIRST_CHILD, H_PARENT };
cv::Mat img = cv::imread("example_image.jpg", cv::IMREAD_UNCHANGED);
// convert RGB to gray scale image
cv::Mat imgGrs;
cv::cvtColor(img, imgGrs, cv::COLOR_RGB2GRAY);
// because it was a .jpg the grey values are messed up
// we fix it by thresholding at 128
cv::threshold(imgGrs, imgGrs, 128, 255, cv::THRESH_BINARY);
imgGrs = ~imgGrs;
// now create contours (we need the hierarchy to find the inner shapes)
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(imgGrs.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
//cv::drawContours(img, contours, -1, cv::Scalar(255, 0, 0), 1);
int iLen = (int)hierarchy.size();
int idxChild = -1;
// find first child of master
for (int i = 0; i < iLen; i++){
if (hierarchy[i][H_PARENT] < 0) {
idxChild = hierarchy[i][H_FIRST_CHILD];
break;
}
}
// used for erosion of mask
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
while (idxChild >= 0)
{
// create image to use as mask for section
cv::Mat mask = cv::Mat::zeros(imgGrs.size(), CV_8U);
cv::drawContours(mask, contours, idxChild, cv::Scalar(255), CV_FILLED);
// make masker 1 pixel smaller so we wont see the outer contours
cv::erode(mask, mask, element);
// ok nu we create a singled out part we want
cv::Mat part = imgGrs & mask;
// Crop it to the AOI rectangle
cv::Rect aoi = cv::boundingRect(contours[idxChild]);
part = part(aoi);
// part is now the aoi image you asked for
// proceed to next AOI
idxChild = hierarchy[idxChild][H_NEXT];
}
return 0;
}
My objective is to rotate an image by a certain angle (e.g. 30 degrees). One possible way of rotating by 90 degrees in OpenCV is given by tenta4 but unfortunately, it only performs 90-degree flips.
Another possible way is a method "SkewGrayImage" given in JavaCV samples where it performs "small angle rotations" that appear to work for rotations of up to approximately 45 - 50 degrees but not for any other higher values.
So - my issues is, is there a proper way/method in OpenCV or JavaCV to actually perform an angular rotation of images or objects?
Meta has explained how to compute a rotation matrix with respect to the center of the image and then to perform a rotation as follows:
Mat rotated_image;
warpAffine(src, rotated_image, rot_mat, src.size());
there is an operation that's called warp, and it is able to just rotate, but also to do some other transformations on the image.
Some useful links are here
https://docs.opencv.org/2.4.13.2/modules/stitching/doc/warpers.html
https://docs.opencv.org/3.1.0/db/d29/group__cudawarping.html
https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html
Hope it helps ;)
A more detailed answer for IplImage rotation is given by Martin based on Mat variables which can then be converted and returned as an IplImage as follows:
Mat source = imread(argv[1], CV_LOAD_IMAGE_COLOR);
Mat rotation_matrix = getRotationMatrix2D(src_center, angle, 1.0);
Mat destinationMat;
warpAffine(source, destinationMat, rotation_matrix, source.size());
IplImage iplframe = IplImage(destinationMat);
Hope this helps! Worked for me with JavaCV.
Mat raw = ... // your raw mat
// Create your "new" Mat and the center of your Raw Mat
Mat result = new Mat(raw.size(), [your Image Type]); // my Img type was CV_8U
Point2f rawCenter = new Point2f(raw.cols() / 2.0F, raw.rows() / 2.0F);
// Scale and Rotation of new Mat
double scale = 1.0;
int rotation = -5;
// Rotation Matrix
Mat rotationMatrix = getRotationMatrix2D(rawCenter, rotation, scale);
// Rotate
warpAffine(raw, result, rotationMatrix, raw.size());
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);
I tried to set a ROI with angle in an image. At first, I thought that using a mask would give me the same result as setting the ROI in my IplImage structure. Then, I would just use cvResize the same way I did when I used ROI, but this time for a mask.
However, this is obviously not this easy, because of the angle.
Is there any way to copy the inside of any rectangle, at any angle, into a new IplImage, which would be of the size of this very rectangle?
CvSeq* approximatedContour = cvApproxPoly(currentContour,
sizeof(CvContour),
0,
CV_POLY_APPROX_DP,
8);
// Circonscrire le polygone trouve dans un rectangle
etiquetteBox = cvMinAreaRect2(approximatedContour);
CvPoint2D32f boxPoints[4];
CvPoint2D32f* c1 = (&cvPoint2D32f(0,0),
&cvPoint2D32f(200,0),
&cvPoint2D32f(0,200),
&cvPoint2D32f(200,200));
CvMat* mmat = cvCreateMat(3,3,CV_32FC1);
cvBoxPoints(etiquetteBox, boxPoints);
IplImage* mask = cvCreateImage(cvSize(in->width,in->height), IPL_DEPTH_8U, 1);
IplImage* ROIimg = cvCreateImage(cvSize(in->width,in->height), IPL_DEPTH_8U, 1);
drawBox(mask,etiquetteBox,target_color[3]);
cvAnd(thresImg,mask,ROIimg,mask);
if(voirSeuillage)
cvCvtColor(ROIimg,in,CV_GRAY2BGR); //ROIimg is OK here!
mmat = cvGetPerspectiveTransform(boxPoints,c1,mmat);
cvWarpPerspective(ROIimg,thresImgResized,mmat); // here I get a full black image!
Doing this, as kindly suggested by Banthar, I get a full black image instead of what is delimited by boxPoints in ROIimg, what's wrong with this code?
After applying answer:
Here is what I do now :
double angle = 0.;
// TODO adaptive angle compensation
if(abs(etiquetteBox.angle) > 30)
angle = etiquetteBox.angle + 270.;
else
angle = etiquetteBox.angle - 270.;
CvPoint2D32f boxPoints[4];
CvPoint2D32f c1[] = {cvPoint2D32f(0,0),
cvPoint2D32f(20,0),
cvPoint2D32f(20,20),
cvPoint2D32f(0,20)};
CvMat* mmat = cvCreateMat(3,3,CV_32FC1);
cvBoxPoints(etiquetteBox, boxPoints);
Point center = Point(10,10);
//warp the image to fit the polygon into the 20x20 image
mmat = cvGetPerspectiveTransform(boxPoints,c1,mmat);
cvWarpPerspective(thresImg,thresImgResized,mmat);
//rotate the image because the inconsistent angle of etiquetteBox
// from a frame to the next ...
//it would be very cool to find a way to fix this...
CvMat rot_mat = getRotationMatrix2D( center, angle,1.0);
cvWarpAffine(thresImgResized,rotatedIm,&rot_mat);
It is still not quite what I want, because the object is rotating into the 20x20 rotatedImg; in thresImgResized, after cvWarpPerspective, the object is well segmented, BUT it is reversed because the inconsistency of the angle of etiquetteBox (-0 degrees in a frame, -90 in the next, depending on how I hold the object to be detected), which I get this way:
cvFindContours(dilImage,
contoursStorage,
&contours,
sizeof(CvContour),
CV_RETR_LIST,
CV_CHAIN_APPROX_TC89_KCOS);
// Trouver des polygones
CvSeq* currentContour = contours;
while(currentContour != 0 && !etiquette)
{
CvSeq* approximatedContour = cvApproxPoly(currentContour,
sizeof(CvContour),
0,
CV_POLY_APPROX_DP,
9);
// Circonscrire le polygone trouve dans un rectangle
etiquetteBox = cvMinAreaRect2(approximatedContour);
I don't know how to fix this, but, at least, it is better than setting my IplImage ROI, because I compensate the etiquetteBox's angle switch from -0 to -90 degrees in consecutive frames.
You are using the wrong brackets in definition of c1. Try this:
CvPoint2D32f c1[] = {
cvPoint2D32f(0,200),
cvPoint2D32f(0,0),
cvPoint2D32f(200,0),
cvPoint2D32f(200,200),
};
I am using following code to stitch to input images. For an unknown
reason the output result is crap!
It seems that the homography matrix is wrong (or is affected wrongly)
because the transformed image is like an "exploited star"!
I have commented the part that I guess is the source of the problem
but I cannot realize it.
Any help or point is appriciated!
Have a nice day,
Ali
void Stitch2Image(IplImage *mImage1, IplImage *mImage2)
{
// Convert input images to gray
IplImage* gray1 = cvCreateImage(cvSize(mImage1->width, mImage1->height), 8, 1);
cvCvtColor(mImage1, gray1, CV_BGR2GRAY);
IplImage* gray2 = cvCreateImage(cvSize(mImage2->width, mImage2->height), 8, 1);
cvCvtColor(mImage2, gray2, CV_BGR2GRAY);
// Convert gray images to Mat
Mat img1(gray1);
Mat img2(gray2);
// Detect FAST keypoints and BRIEF features in the first image
FastFeatureDetector detector(50);
BriefDescriptorExtractor descriptorExtractor;
BruteForceMatcher<L1<uchar> > descriptorMatcher;
vector<KeyPoint> keypoints1;
detector.detect( img1, keypoints1 );
Mat descriptors1;
descriptorExtractor.compute( img1, keypoints1, descriptors1 );
/* Detect FAST keypoints and BRIEF features in the second image*/
vector<KeyPoint> keypoints2;
detector.detect( img1, keypoints2 );
Mat descriptors2;
descriptorExtractor.compute( img2, keypoints2, descriptors2 );
vector<DMatch> matches;
descriptorMatcher.match(descriptors1, descriptors2, matches);
if (matches.size()==0)
return;
vector<Point2f> points1, points2;
for(size_t q = 0; q < matches.size(); q++)
{
points1.push_back(keypoints1[matches[q].queryIdx].pt);
points2.push_back(keypoints2[matches[q].trainIdx].pt);
}
// Create the result image
result = cvCreateImage(cvSize(mImage2->width * 2, mImage2->height), 8, 3);
cvZero(result);
// Copy the second image in the result image
cvSetImageROI(result, cvRect(mImage2->width, 0, mImage2->width, mImage2->height));
cvCopy(mImage2, result);
cvResetImageROI(result);
// Create warp image
IplImage* warpImage = cvCloneImage(result);
cvZero(warpImage);
/************************** Is there anything wrong here!? *******************/
// Find homography matrix
Mat H = findHomography(Mat(points1), Mat(points2), 8, 3.0);
CvMat HH = H; // Is this line converted correctly?
// Transform warp image
cvWarpPerspective(mImage1, warpImage, &HH);
// Blend
blend(result, warpImage);
/*******************************************************************************/
cvReleaseImage(&gray1);
cvReleaseImage(&gray2);
cvReleaseImage(&warpImage);
}
This is what I would suggest you to try, in this order:
1) Use CV_RANSAC option for homography. Refer http://opencv.willowgarage.com/documentation/cpp/calib3d_camera_calibration_and_3d_reconstruction.html
2) Try other descriptors, particularly SIFT or SURF which ship with OpenCV. For some images FAST or BRIEF descriptors are not discriminating enough. EDIT (Aug '12): The ORB descriptors, which are based on BRIEF, are quite good and fast!
3) Try to look at the Homography matrix (step through in debug mode or print it) and see if it is consistent.
4) If above does not give you a clue, try to look at the matches that are formed. Is it matching one point in one image with a number of points in the other image? If so the problem again should be with the descriptors or the detector.
My hunch is that it is the descriptors (so 1) or 2) should fix it).
Also switch to Hamming distance instead of L1 distance in BruteForceMatcher. BRIEF descriptors are supposed to be compared using Hamming distance.
Your homography, might calculated based on wrong matches and thus represent bad allignment.
I suggest to path the matrix through additional check of interdependancy between its rows.
You can use the following code:
bool cvExtCheckTransformValid(const Mat& T){
// Check the shape of the matrix
if (T.empty())
return false;
if (T.rows != 3)
return false;
if (T.cols != 3)
return false;
// Check for linear dependency.
Mat tmp;
T.row(0).copyTo(tmp);
tmp /= T.row(1);
Scalar mean;
Scalar stddev;
meanStdDev(tmp,mean,stddev);
double X = abs(stddev[0]/mean[0]);
printf("std of H:%g\n",X);
if (X < 0.8)
return false;
return true;
}