blo*_*mir 5 opencv perspective
好吧,我不得不承认我是 OpenCV 的新手,我的 MATLAB/lin. 代数知识可能会引入偏见。但是我想做的事情真的很简单,而我仍然没有设法找到答案。
在透视变换下尝试校正图像(或图像的一部分)时,您基本上执行两个步骤(假设您有 4 个定义扭曲对象的点):
findHomography()或getPerspectiveTransform()- 为什么这两个在同一点上的操作不同是另一回事,也令人沮丧);这给了我们一个矩阵 T。warpPerspective())。现在,最后一个函数 ( warpPerspective()) 要求用户指定目标图像的大小。
我的问题是用户应该如何事先知道该尺寸是多少。这样做的低级方法是简单地将变换 T 应用于找到对象的图像的角点,从而保证您不会超出新变换形状的边界。但是,即使您从 T 中取出矩阵并手动将其应用于这些点,结果看起来也很奇怪。
有没有办法在 OpenCV 中做到这一点?谢谢!
PS下面是一些代码:
float leftX, lowerY, rightX, higherY;
float minX = std::numeric_limits<float>::max(), maxX = std::numeric_limits<float>::min(), minY = std::numeric_limits<float>::max(), maxY = std::numeric_limits<float>::min();
Mat value, pt;
for(int i=0; i<4; i++)
{
switch(i)
{
case 0:
pt = (Mat_<float>(3, 1) << 1.00,1.00,1.00);
break;
case 1:
pt = (Mat_<float>(3, 1) << srcIm.cols,1.00,1.00);
break;
case 2:
pt = (Mat_<float>(3, 1) << 1.00,srcIm.rows,1.00);
break;
case 3:
pt = (Mat_<float>(3, 1) << srcIm.cols,srcIm.rows,1.00);
break;
default:
cerr << "Wrong switch." << endl;
break;
}
value = invH*pt;
value /= value.at<float>(2);
minX = min(minX,value.at<float>(0));
maxX = max(maxX,value.at<float>(0));
minY = min(minY,value.at<float>(1));
maxY = max(maxY,value.at<float>(1));
}
leftX = std::min<float>(1.00,-minX);
lowerY = std::min<float>(1.00,-minY);
rightX = max(srcIm.cols-minX,maxX-minX);
higherY = max(srcIm.rows-minY,maxY-minY);
warpPerspective(srcIm, dstIm, H, Size(rightX-leftX,higherY-lowerY), cv::INTER_CUBIC);
Run Code Online (Sandbox Code Playgroud)
更新:也许我的结果看起来不太好,因为我使用的矩阵是错误的。由于我无法观察里面发生了什么getPerspectiveTransform(),我无法知道这个矩阵是如何计算的,但它有一些非常小的和非常大的值,这让我觉得它们是垃圾。这是我从 T 获取数据的方式:
for(int row=0;row<3;row++)
for(int col=0;col<3;col++)
T.at<float>(row,col) = ((float*)(H.data + (size_t)H.step*row))[col];
Run Code Online (Sandbox Code Playgroud)
(尽管来自 的输出矩阵getPerspectiveTransform()是 3x3,但尝试通过直接访问其值T.at<float>(row,col)会导致分段错误。)
这是正确的方法吗?也许这就是原始问题出现的原因,因为我没有得到正确的矩阵......
小智 6
如果您在调用 warpPerspective 之前就知道图像的大小,那么您可以获取其四个角的坐标,并使用perspectiveTransform 对它们进行变换,以查看它们在变换后的效果。据推测,它们将不再形成一个漂亮的矩形,因此您可能需要计算最小值和最大值以获得边界框。然后,这个边界框的大小就是你想要的目标大小。(此外,如果任何角低于零,请不要忘记根据需要平移框。)这是一个 Python 示例,它使用 warpPerspective 将变换后的图像置于其自身之上。
from typing import Tuple
import cv2
import numpy as np
import math
# Input: a source image and perspective transform
# Output: a warped image and 2 translation terms
def perspective_warp(image: np.ndarray, transform: np.ndarray) -> Tuple[np.ndarray, int, int]:
h, w = image.shape[:2]
corners_bef = np.float32([[0, 0], [w, 0], [w, h], [0, h]]).reshape(-1, 1, 2)
corners_aft = cv2.perspectiveTransform(corners_bef, transform)
xmin = math.floor(corners_aft[:, 0, 0].min())
ymin = math.floor(corners_aft[:, 0, 1].min())
xmax = math.ceil(corners_aft[:, 0, 0].max())
ymax = math.ceil(corners_aft[:, 0, 1].max())
x_adj = math.floor(xmin - corners_aft[0, 0, 0])
y_adj = math.floor(ymin - corners_aft[0, 0, 1])
translate = np.eye(3)
translate[0, 2] = -xmin
translate[1, 2] = -ymin
corrected_transform = np.matmul(translate, transform)
return cv2.warpPerspective(image, corrected_transform, (math.ceil(xmax - xmin), math.ceil(ymax - ymin))), x_adj, y_adj
# Just like perspective_warp, but it also returns an alpha mask that can be used for blitting
def perspective_warp_with_mask(image: np.ndarray, transform: np.ndarray) -> Tuple[np.ndarray, np.ndarray, int, int]:
mask_in = np.empty(image.shape, dtype = np.uint8)
mask_in.fill(255)
output, x_adj, y_adj = perspective_warp(image, transform)
mask, _, _ = perspective_warp(mask_in, transform)
return output, mask, x_adj, y_adj
# alpha_blits src onto dest according to the alpha values in mask at location (x, y),
# ignoring any parts that do not overlap
def alpha_blit(dest: np.ndarray, src: np.ndarray, mask: np.ndarray, x: int, y: int) -> None:
dl = max(x, 0)
dt = max(y, 0)
sl = max(-x, 0)
st = max(-y, 0)
sr = max(sl, min(src.shape[1], dest.shape[1] - x))
sb = max(st, min(src.shape[0], dest.shape[0] - y))
dr = dl + sr - sl
db = dt + sb - st
m = mask[st:sb, sl:sr]
dest[dt:db, dl:dr] = (dest[dt:db, dl:dr].astype(np.float) * (255 - m) + src[st:sb, sl:sr].astype(np.float) * m) / 255
# blits a perspective-warped src image onto dest
def perspective_blit(dest: np.ndarray, src: np.ndarray, transform: np.ndarray) -> None:
blitme, mask, x_adj, y_adj = perspective_warp_with_mask(src, transform)
cv2.imwrite("blitme.png", blitme)
alpha_blit(dest, blitme, mask, int(transform[0, 2] + x_adj), int(transform[1, 2] + y_adj))
# Read an input image
image: np.array = cv2.imread('input.jpg')
# Make a perspective transform
h, w = image.shape[:2]
corners_in = np.float32([[[0, 0]], [[w, 0]], [[w, h]], [[0, h]]])
corners_out = np.float32([[[100, 100]], [[300, -100]], [[500, 300]], [[-50, 500]]])
transform = cv2.getPerspectiveTransform(corners_in, corners_out)
# Blit the warped image on top of the original
perspective_blit(image, image, transform)
cv2.imwrite('output.jpg', image)
Run Code Online (Sandbox Code Playgroud)
结果示例:
如果结果看起来很奇怪,可能是因为您的点在 getPerspectiveTransform 中设置不正确。您的点向量必须按正确的顺序排列(左上、右上、右下、左下)。
但要回答您最初的问题,不存在“最佳输出大小”之类的东西。你必须根据你想做什么来决定。尝试并尝试找到适合您的尺寸。
编辑 :
如果问题出在变换矩阵上,那么如何创建它?openCV 中的一个好方法是:
vector<Point2f> corners;
corners.push_back(topleft);
corners.push_back(topright);
corners.push_back(bottomright);
corners.push_back(bottomleft);
// Corners of the destination image
// output is the output image, should be defined before this operation
vector<cv::Point2f> output_corner;
output_corner.push_back(cv::Point2f(0, 0));
output_corner.push_back(cv::Point2f(output.cols, 0));
output_corner.push_back(cv::Point2f(output.cols, output.rows));
output_corner.push_back(cv::Point2f(0, output.rows));
// Get transformation matrix
Mat H = getPerspectiveTransform(corners, output_corner);
Run Code Online (Sandbox Code Playgroud)