Rotate an image without cropping in OpenCV in C++
My answer is inspired by the following posts / blog entries:
- Rotate cv::Mat using cv::warpAffine offsets destination image
- http://john.freml.in/opencv-rotation
Main ideas:
- Adjusting the rotation matrix by adding a translation to the new image center
- Using
cv::RotatedRect
to rely on existing opencv functionality as much as possible
Code tested with opencv 3.4.1:
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("im.png", CV_LOAD_IMAGE_UNCHANGED);
double angle = -45;
// get rotation matrix for rotating the image around its center in pixel coordinates
cv::Point2f center((src.cols-1)/2.0, (src.rows-1)/2.0);
cv::Mat rot = cv::getRotationMatrix2D(center, angle, 1.0);
// determine bounding rectangle, center not relevant
cv::Rect2f bbox = cv::RotatedRect(cv::Point2f(), src.size(), angle).boundingRect2f();
// adjust transformation matrix
rot.at<double>(0,2) += bbox.width/2.0 - src.cols/2.0;
rot.at<double>(1,2) += bbox.height/2.0 - src.rows/2.0;
cv::Mat dst;
cv::warpAffine(src, dst, rot, bbox.size());
cv::imwrite("rotated_im.png", dst);
return 0;
}
Rotate image without cropping OpenCV
Create new square image with dimension = diagonal of your initial image.
Draw initial image into the center of new image.
Rotate new image
Image Rotation without cropping
The rotated image usually has to be large than the old image to store all pixel values.
Each point (x,y)
is translated to
(x', y') = (x*cos(rads) - y*sin(rads), x*sin(rads) + y*cos(rads))
An image with height h
and width w
, center at (0,0) and corners at
(h/2, w/2)
(h/2, -w/2)
(-h/2, w/2)
(-h/2, -w/2)
has a new height of
h' = 2*y' = 2 * (w/2*sin(rads) + h/2*cos(rads))
and a new width of
w' = 2*x' = 2 * (w/2*cos(rads) + h/2*sin(rads))
for 0 <= rads <= pi/4
. It is x * y <= x' * y'
and for rads != k*pi/2
with k = 1, 2, ...
it is x * y < x' * y'
In any case the area of the rotated image is same size as or larger than the area of the old image.
If you use the old size, you cut off the corners.
Example:
Your image has h=1
, w=1
and rads=pi/4
. You need a new image with h'=sqrt(2)=1.41421356237
and w'=sqrt(2)=1.41421356237
to store all pixel values. The pixel from (1,1) is translated to (0, sqrt(2)).
Rotate an image using OpenCV with C++ on interface
try this.worked.
IplImage* source_image;
IplImage *dest = cvCloneImage(source_image);
CvPoint2D32f center;
center.x = dest->width / 2;
center.y = dest->height / 2;
CvMat *mapMatrix = cvCreateMat(2, 3, CV_32FC1);
double angle = System::Convert::ToDouble(numericUpDown1->Value);
cv2DRotationMatrix(center, angle, 1.0, mapMatrix);
cvWarpAffine(source_image, dest, mapMatrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0));
cvReleaseMat(&mapMatrix);
cvShowImage("Rotated", dest);
Related Topics
Can Virtual Functions Have Default Parameters
C++ Obtaining Milliseconds Time on Linux - Clock() Doesn't Seem to Work Properly
Dual Emission of Constructor Symbols
Magic Number in Boost::Hash_Combine
How to Propagate Exceptions Between Threads
Global Variable Within Multiple Files
What Are the Rules For Automatic Generation of Move Operations
How to Printf Uint64_T? Fails With: "Spurious Trailing '%' in Format"
How to Read Until Eof from Cin in C++
How to Convert Std::String to Lpcwstr in C++ (Unicode)
How to Assign an Alias to a Function Name in C++
What Is _Gxx_Personality_V0 For
How to Create My Own Comparator For a Map
Why Is Default Constructor Called in Virtual Inheritance
Experimental::Filesystem Linker Error
Why Can Lambdas Be Better Optimized by the Compiler Than Plain Functions