OpenCV ROI 越界:用黑色填充?
OpenCV ROI Out-of-bounds: Fill with black?
我正在使用 OpenCV 从大图像中获取小的矩形 ROI,并将 ROI 保存到文件中。有时,ROI 会超出图像范围。我需要一种方法让生成的 Mat 显示大图像中边界内的部分,并为其余部分显示黑色。
为了帮助解释,假设您有一张区域地图图像。我知道一个人在地图上的位置,并想在地图上截取 500x500 像素的部分,并以他们的位置为中心。但是当用户到达地图的边缘时,这个 500x500 部分的一部分将需要 "off the map"。所以我希望它用黑色填充它。
OpenCV 最好能够通过用黑色填充(就像用 warpAffine
旋转图像时那样)来优雅地处理越界 ROI(例如负左上角值),但是似乎并非如此。对如何实现这个目标有什么建议吗?
我不确定是否有函数可以为您执行此操作,但您自己执行此操作非常简单 - 我创建了一个类似的函数来限制 ROI。您只需要将 ROI 的左上角位置与图像边框进行比较即可。
例如检查
if(roi.bottomRight.x > image.bottomRight.x)
constrain.x = image.bottomRight.x - roi.topLeft.x
这是您在 ROI 矩阵中的访问点,包含跟踪的图像。您需要为每个边界执行此操作。现在您可以访问这些边界内的 ROI 矩阵并将像素值设置为 (0,0,0)。
另一种解决方法是创建第二个矩形(图像大小)并使用矩形运算符进行交集(rect = rect1 & rect2
)。然后,通过减去跟踪矩形和相交矩形的宽度和高度,您立即获得了约束,并且您能够执行与我上面提到的相同的矩阵访问。如果这是一个可能的替代解决方案——你可以只使用没有黑色区域的交叉矩形——那么你只需要复制交叉矩形大小范围内的值。
我发现最好的方法是获取边界内的 ROI 部分,然后计算 ROI 每一侧 (top/bottom/left/right) 有多少超出边界,然后使用 copyMakeBorder
函数在每边填充那么多黑色边框。结果很好。现在看起来像这样:
Mat getPaddedROI(const Mat &input, int top_left_x, int top_left_y, int width, int height, Scalar paddingColor) {
int bottom_right_x = top_left_x + width;
int bottom_right_y = top_left_y + height;
Mat output;
if (top_left_x < 0 || top_left_y < 0 || bottom_right_x > input.cols || bottom_right_y > input.rows) {
// border padding will be required
int border_left = 0, border_right = 0, border_top = 0, border_bottom = 0;
if (top_left_x < 0) {
width = width + top_left_x;
border_left = -1 * top_left_x;
top_left_x = 0;
}
if (top_left_y < 0) {
height = height + top_left_y;
border_top = -1 * top_left_y;
top_left_y = 0;
}
if (bottom_right_x > input.cols) {
width = width - (bottom_right_x - input.cols);
border_right = bottom_right_x - input.cols;
}
if (bottom_right_y > input.rows) {
height = height - (bottom_right_y - input.rows);
border_bottom = bottom_right_y - input.rows;
}
Rect R(top_left_x, top_left_y, width, height);
copyMakeBorder(input(R), output, border_top, border_bottom, border_left, border_right, BORDER_CONSTANT, paddingColor);
}
else {
// no border padding required
Rect R(top_left_x, top_left_y, width, height);
output = input(R);
}
return output;
}
而且您可以轻松地将内边距设为您喜欢的任何颜色,这很好。
所有其他答案对我来说似乎有点太复杂了。只需:
// Create rects representing the image and the ROI
auto image_rect = cv::Rect({}, image.size());
auto roi = cv::Rect(-50, 50, 200, 100)
// Find intersection, i.e. valid crop region
auto intersection = image_rect & roi;
// Move intersection to the result coordinate space
auto inter_roi = intersection - roi.tl();
// Create black image and copy intersection
cv::Mat crop = cv::Mat::zeros(roi.size(), image.type());
image(intersection).copyTo(crop(inter_roi));
参考图片:
我正在使用 OpenCV 从大图像中获取小的矩形 ROI,并将 ROI 保存到文件中。有时,ROI 会超出图像范围。我需要一种方法让生成的 Mat 显示大图像中边界内的部分,并为其余部分显示黑色。
为了帮助解释,假设您有一张区域地图图像。我知道一个人在地图上的位置,并想在地图上截取 500x500 像素的部分,并以他们的位置为中心。但是当用户到达地图的边缘时,这个 500x500 部分的一部分将需要 "off the map"。所以我希望它用黑色填充它。
OpenCV 最好能够通过用黑色填充(就像用 warpAffine
旋转图像时那样)来优雅地处理越界 ROI(例如负左上角值),但是似乎并非如此。对如何实现这个目标有什么建议吗?
我不确定是否有函数可以为您执行此操作,但您自己执行此操作非常简单 - 我创建了一个类似的函数来限制 ROI。您只需要将 ROI 的左上角位置与图像边框进行比较即可。
例如检查
if(roi.bottomRight.x > image.bottomRight.x)
constrain.x = image.bottomRight.x - roi.topLeft.x
这是您在 ROI 矩阵中的访问点,包含跟踪的图像。您需要为每个边界执行此操作。现在您可以访问这些边界内的 ROI 矩阵并将像素值设置为 (0,0,0)。
另一种解决方法是创建第二个矩形(图像大小)并使用矩形运算符进行交集(rect = rect1 & rect2
)。然后,通过减去跟踪矩形和相交矩形的宽度和高度,您立即获得了约束,并且您能够执行与我上面提到的相同的矩阵访问。如果这是一个可能的替代解决方案——你可以只使用没有黑色区域的交叉矩形——那么你只需要复制交叉矩形大小范围内的值。
我发现最好的方法是获取边界内的 ROI 部分,然后计算 ROI 每一侧 (top/bottom/left/right) 有多少超出边界,然后使用 copyMakeBorder
函数在每边填充那么多黑色边框。结果很好。现在看起来像这样:
Mat getPaddedROI(const Mat &input, int top_left_x, int top_left_y, int width, int height, Scalar paddingColor) {
int bottom_right_x = top_left_x + width;
int bottom_right_y = top_left_y + height;
Mat output;
if (top_left_x < 0 || top_left_y < 0 || bottom_right_x > input.cols || bottom_right_y > input.rows) {
// border padding will be required
int border_left = 0, border_right = 0, border_top = 0, border_bottom = 0;
if (top_left_x < 0) {
width = width + top_left_x;
border_left = -1 * top_left_x;
top_left_x = 0;
}
if (top_left_y < 0) {
height = height + top_left_y;
border_top = -1 * top_left_y;
top_left_y = 0;
}
if (bottom_right_x > input.cols) {
width = width - (bottom_right_x - input.cols);
border_right = bottom_right_x - input.cols;
}
if (bottom_right_y > input.rows) {
height = height - (bottom_right_y - input.rows);
border_bottom = bottom_right_y - input.rows;
}
Rect R(top_left_x, top_left_y, width, height);
copyMakeBorder(input(R), output, border_top, border_bottom, border_left, border_right, BORDER_CONSTANT, paddingColor);
}
else {
// no border padding required
Rect R(top_left_x, top_left_y, width, height);
output = input(R);
}
return output;
}
而且您可以轻松地将内边距设为您喜欢的任何颜色,这很好。
所有其他答案对我来说似乎有点太复杂了。只需:
// Create rects representing the image and the ROI
auto image_rect = cv::Rect({}, image.size());
auto roi = cv::Rect(-50, 50, 200, 100)
// Find intersection, i.e. valid crop region
auto intersection = image_rect & roi;
// Move intersection to the result coordinate space
auto inter_roi = intersection - roi.tl();
// Create black image and copy intersection
cv::Mat crop = cv::Mat::zeros(roi.size(), image.type());
image(intersection).copyTo(crop(inter_roi));
参考图片: