Is there a way of resizing images of any shape or size to say [500x500]
but have the image\'s aspect ratio be maintained, levaing the empty space be filled with whi
You can create another image of the square size you wish, then put your image in the middle of the square image. Something like this:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
int main(int argc, char *argv[])
{
// read an image
cv::Mat image1= cv::imread("/home/hdang/Desktop/colorCode.png");
//resize it
cv::Size newSize = cv::Size(image1.cols/2,image1.rows/2);
cv::resize(image1, image1, newSize, 0, 0, cv::INTER_LINEAR);
//create the square container
int dstWidth = 500;
int dstHeight = 500;
cv::Mat dst = cv::Mat(dstHeight, dstWidth, CV_8UC3, cv::Scalar(0,0,0));
//Put the image into the container, roi is the new position
cv::Rect roi(cv::Rect(0,dst.rows*0.25,image1.cols,image1.rows));
cv::Mat targetROI = dst(roi);
image1.copyTo(targetROI);
//View the result
cv::namedWindow("OpenCV Window");
cv::imshow("OpenCV Window", dst);
// wait key for 5000 ms
cv::waitKey(5000);
return 0;
}
Not fully optimized, but you can try this:
EDIT handle target size that is not 500x500
pixels and wrapping it up as a function.
cv::Mat GetSquareImage( const cv::Mat& img, int target_width = 500 )
{
int width = img.cols,
height = img.rows;
cv::Mat square = cv::Mat::zeros( target_width, target_width, img.type() );
int max_dim = ( width >= height ) ? width : height;
float scale = ( ( float ) target_width ) / max_dim;
cv::Rect roi;
if ( width >= height )
{
roi.width = target_width;
roi.x = 0;
roi.height = height * scale;
roi.y = ( target_width - roi.height ) / 2;
}
else
{
roi.y = 0;
roi.height = target_width;
roi.width = width * scale;
roi.x = ( target_width - roi.width ) / 2;
}
cv::resize( img, square( roi ), roi.size() );
return square;
}
Alireza's answer is good, however I modified the code slightly so that I don't add the vertical borders when the image fits vertically and I don't add horizontal borders when the image fits horizontally (this is closer to the original request):
cv::Mat utilites::resizeKeepAspectRatio(const cv::Mat &input, const cv::Size &dstSize, const cv::Scalar &bgcolor)
{
cv::Mat output;
// initially no borders
int top = 0;
int down = 0;
int left = 0;
int right = 0;
if( h1 <= dstSize.height)
{
// only vertical borders
top = (dstSize.height - h1) / 2;
down = top;
cv::resize( input, output, cv::Size(dstSize.width, h1));
}
else
{
// only horizontal borders
left = (dstSize.width - w2) / 2;
right = left;
cv::resize( input, output, cv::Size(w2, dstSize.height));
}
return output;
}
A general approach:
cv::Mat utilites::resizeKeepAspectRatio(const cv::Mat &input, const cv::Size &dstSize, const cv::Scalar &bgcolor)
{
cv::Mat output;
double h1 = dstSize.width * (input.rows/(double)input.cols);
double w2 = dstSize.height * (input.cols/(double)input.rows);
if( h1 <= dstSize.height) {
cv::resize( input, output, cv::Size(dstSize.width, h1));
} else {
cv::resize( input, output, cv::Size(w2, dstSize.height));
}
int top = (dstSize.height-output.rows) / 2;
int down = (dstSize.height-output.rows+1) / 2;
int left = (dstSize.width - output.cols) / 2;
int right = (dstSize.width - output.cols+1) / 2;
cv::copyMakeBorder(output, output, top, down, left, right, cv::BORDER_CONSTANT, bgcolor );
return output;
}