将图像调整为正方形但保持纵横比 c++ opencv
Resize an image to a square but keep aspect ratio c++ opencv
有没有办法调整任何形状或大小的图像 [500x500]
但保持图像的宽高比,让空白 space 填充 white/black 填充物?
假设图像是 [2000x1000]
,在调整大小到 [500x500]
后,实际图像本身将是 [500x250]
,125
两边是 white/black 填充物。
像这样:
输入
输出
编辑
我不想简单地在正方形中显示图像 window,而是将图像更改为该状态,然后保存到文件中,创建一个大小相同且图像失真尽可能小的图像集合可能。
我遇到的唯一问类似问题的是 this post,但在 php
中。
没有完全优化,但你可以试试这个:
EDIT 处理不是 500x500
像素的目标大小并将其包装为一个函数。
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;
}
您可以创建另一个您想要的正方形大小的图像,然后将您的图像放在正方形图像的中间。像这样:
#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;
}
一般方法:
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;
}
Alireza 的回答很好,但是我稍微修改了代码,这样当图像垂直适合时我不添加垂直边框,当图像水平适合时我不添加水平边框(这更接近于原始请求):
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;
}
我扩展了 alireza 答案以允许零分配答案。
- 允许用户给出一个预分配的,或一个
cv::Mat
作为输入
cv::resize
输入图像立即输出mat
- 用
cv::rectangle
给顶部和底部框上色
#include <opencv2/imgproc.hpp>
void resizeKeepAspectRatio(const cv::Mat& src, cv::Mat& dst, const cv::Size& dstSize, const cv::Scalar& backgroundColor = {})
{
// Don't handle anything in this corner case
if(dstSize.width <= 0 || dstSize.height <= 0)
return;
// Not job is needed here, let's avoid any copy
if(src.cols == dstSize.width && src.rows == dstSize.height)
{
dst = src;
return;
}
// Try not to reallocate memory if possible
cv::Mat output = [&]()
{
if(dst.data != src.data && dst.cols == dstSize.width && dst.rows == dstSize.height && dst.type() == src.type())
return dst;
return cv::Mat(dstSize.height, dstSize.width, src.type());
}();
// 'src' inside 'dst'
const auto imageBox = [&]()
{
const auto h1 = int(dstSize.width * (src.rows / (double)src.cols));
const auto w2 = int(dstSize.height * (src.cols / (double)src.rows));
const bool horizontal = h1 <= dstSize.height;
const auto width = horizontal ? dstSize.width : w2;
const auto height = horizontal ? h1 : dstSize.height;
const auto x = horizontal ? 0 : int(double(dstSize.width - width) / 2.);
const auto y = horizontal ? int(double(dstSize.height - height) / 2.) : 0;
return cv::Rect(x, y, width, height);
}();
cv::Rect firstBox;
cv::Rect secondBox;
if(imageBox.width > imageBox.height)
{
// ┌──────────────► x
// │ ┌────────────┐
// │ │┼┼┼┼┼┼┼┼┼┼┼┼│ firstBox
// │ x────────────►
// │ │ │
// │ ▼────────────┤
// │ │┼┼┼┼┼┼┼┼┼┼┼┼│ secondBox
// │ └────────────┘
// ▼
// y
firstBox.x = 0;
firstBox.width = dstSize.width;
firstBox.y = 0;
firstBox.height = imageBox.y;
secondBox.x = 0;
secondBox.width = dstSize.width;
secondBox.y = imageBox.y + imageBox.height;
secondBox.height = dstSize.height - secondBox.y;
}
else
{
// ┌──────────────► x
// │ ┌──x──────►──┐
// │ │┼┼│ │┼┼│
// │ │┼┼│ │┼┼│
// │ │┼┼│ │┼┼│
// │ └──▼──────┴──┘
// ▼ firstBox secondBox
// y
firstBox.y = 0;
firstBox.height = dstSize.height;
firstBox.x = 0;
firstBox.width = imageBox.x;
secondBox.y = 0;
secondBox.height = dstSize.height;
secondBox.x = imageBox.x + imageBox.width;
secondBox.width = dstSize.width - secondBox.x;
}
// Resizing to final image avoid useless memory allocation
cv::Mat outputImage = output(imageBox);
assert(outputImage.cols == imageBox.width);
assert(outputImage.rows == imageBox.height);
const auto* dataBeforeResize = outputImage.data;
cv::resize(src, outputImage, cv::Size(outputImage.cols, outputImage.rows));
assert(dataBeforeResize == outputImage.data);
const auto drawBox = [&](const cv::Rect& box)
{
if(box.width > 0 && box.height > 0)
{
cv::rectangle(output, cv::Point(box.x, box.y), cv::Point(box.x + box.width, box.y + box.height), backgroundColor, -1);
}
};
drawBox(firstBox);
drawBox(secondBox);
// Finally copy output to dst, like that user can use src & dst to the same cv::Mat
dst = output;
}
有了这个功能,dst
垫子可以重复使用而无需重新分配。
cv::Mat src(200, 100, CV_8UC3, cv::Scalar(1,100,200));
cv::Size dstSize(300, 400)
cv::Mat dst;
resizeKeepAspectRatio(src, dst, dstSize); // dst get allocated
resizeKeepAspectRatio(src, dst, dstSize); // dst get reused
有没有办法调整任何形状或大小的图像 [500x500]
但保持图像的宽高比,让空白 space 填充 white/black 填充物?
假设图像是 [2000x1000]
,在调整大小到 [500x500]
后,实际图像本身将是 [500x250]
,125
两边是 white/black 填充物。
像这样:
输入
输出
编辑
我不想简单地在正方形中显示图像 window,而是将图像更改为该状态,然后保存到文件中,创建一个大小相同且图像失真尽可能小的图像集合可能。
我遇到的唯一问类似问题的是 this post,但在 php
中。
没有完全优化,但你可以试试这个:
EDIT 处理不是 500x500
像素的目标大小并将其包装为一个函数。
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;
}
您可以创建另一个您想要的正方形大小的图像,然后将您的图像放在正方形图像的中间。像这样:
#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;
}
一般方法:
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;
}
Alireza 的回答很好,但是我稍微修改了代码,这样当图像垂直适合时我不添加垂直边框,当图像水平适合时我不添加水平边框(这更接近于原始请求):
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;
}
我扩展了 alireza 答案以允许零分配答案。
- 允许用户给出一个预分配的,或一个
cv::Mat
作为输入 cv::resize
输入图像立即输出mat- 用
cv::rectangle
给顶部和底部框上色
#include <opencv2/imgproc.hpp>
void resizeKeepAspectRatio(const cv::Mat& src, cv::Mat& dst, const cv::Size& dstSize, const cv::Scalar& backgroundColor = {})
{
// Don't handle anything in this corner case
if(dstSize.width <= 0 || dstSize.height <= 0)
return;
// Not job is needed here, let's avoid any copy
if(src.cols == dstSize.width && src.rows == dstSize.height)
{
dst = src;
return;
}
// Try not to reallocate memory if possible
cv::Mat output = [&]()
{
if(dst.data != src.data && dst.cols == dstSize.width && dst.rows == dstSize.height && dst.type() == src.type())
return dst;
return cv::Mat(dstSize.height, dstSize.width, src.type());
}();
// 'src' inside 'dst'
const auto imageBox = [&]()
{
const auto h1 = int(dstSize.width * (src.rows / (double)src.cols));
const auto w2 = int(dstSize.height * (src.cols / (double)src.rows));
const bool horizontal = h1 <= dstSize.height;
const auto width = horizontal ? dstSize.width : w2;
const auto height = horizontal ? h1 : dstSize.height;
const auto x = horizontal ? 0 : int(double(dstSize.width - width) / 2.);
const auto y = horizontal ? int(double(dstSize.height - height) / 2.) : 0;
return cv::Rect(x, y, width, height);
}();
cv::Rect firstBox;
cv::Rect secondBox;
if(imageBox.width > imageBox.height)
{
// ┌──────────────► x
// │ ┌────────────┐
// │ │┼┼┼┼┼┼┼┼┼┼┼┼│ firstBox
// │ x────────────►
// │ │ │
// │ ▼────────────┤
// │ │┼┼┼┼┼┼┼┼┼┼┼┼│ secondBox
// │ └────────────┘
// ▼
// y
firstBox.x = 0;
firstBox.width = dstSize.width;
firstBox.y = 0;
firstBox.height = imageBox.y;
secondBox.x = 0;
secondBox.width = dstSize.width;
secondBox.y = imageBox.y + imageBox.height;
secondBox.height = dstSize.height - secondBox.y;
}
else
{
// ┌──────────────► x
// │ ┌──x──────►──┐
// │ │┼┼│ │┼┼│
// │ │┼┼│ │┼┼│
// │ │┼┼│ │┼┼│
// │ └──▼──────┴──┘
// ▼ firstBox secondBox
// y
firstBox.y = 0;
firstBox.height = dstSize.height;
firstBox.x = 0;
firstBox.width = imageBox.x;
secondBox.y = 0;
secondBox.height = dstSize.height;
secondBox.x = imageBox.x + imageBox.width;
secondBox.width = dstSize.width - secondBox.x;
}
// Resizing to final image avoid useless memory allocation
cv::Mat outputImage = output(imageBox);
assert(outputImage.cols == imageBox.width);
assert(outputImage.rows == imageBox.height);
const auto* dataBeforeResize = outputImage.data;
cv::resize(src, outputImage, cv::Size(outputImage.cols, outputImage.rows));
assert(dataBeforeResize == outputImage.data);
const auto drawBox = [&](const cv::Rect& box)
{
if(box.width > 0 && box.height > 0)
{
cv::rectangle(output, cv::Point(box.x, box.y), cv::Point(box.x + box.width, box.y + box.height), backgroundColor, -1);
}
};
drawBox(firstBox);
drawBox(secondBox);
// Finally copy output to dst, like that user can use src & dst to the same cv::Mat
dst = output;
}
有了这个功能,dst
垫子可以重复使用而无需重新分配。
cv::Mat src(200, 100, CV_8UC3, cv::Scalar(1,100,200));
cv::Size dstSize(300, 400)
cv::Mat dst;
resizeKeepAspectRatio(src, dst, dstSize); // dst get allocated
resizeKeepAspectRatio(src, dst, dstSize); // dst get reused