将四边形图像提取为矩形

16

A photo

赏金更新

根据Denis的链接,以下是如何使用threeblindmiceandamonkey代码:

// the destination rect is our 'in' quad
int dw = 300, dh = 250;
double in[4][4] = {{0,0},{dw,0},{dw,dh},{0,dh}};
    // the quad in the source image is our 'out'
double out[4][5] = {{171,72},{331,93},{333,188},{177,210}};
double homo[3][6];
const int ret = mapQuadToQuad(in,out,homo);
    // homo can be used for calculating the x,y of any destination point
// in the source, e.g.
for(int i=0; i<4; i++) {
    double p1[3] = {out[i][0],out[i][7],1};
    double p2[3];
    transformMatrix(p1,p2,homo);
    p2[0] /= p2[2]; // x
    p2[1] /= p2[2]; // y
    printf("\t%2.2f\t%2.2f\n",p2[0],p2[1]);
}

这提供了一种将目标点转换为源点的变换方式 - 当然您也可以反过来进行,但能够混合进行这样做会更加整洁:

for(int y=0; y<dh; y++) {
    for(int x=0; x<dw; x++) {
        // calc the four corners in source for this
        // destination pixel, and mix

对于混合,我使用超级采样和随机点; 它非常有效,即使源区域和目标区域存在很大差异


背景问题

在顶部的图像中,货车侧面的标志不是正对着相机的。我想尽可能准确地计算出它正对着相机时的样子。

我知道图像中四边形的角落坐标和目标矩形的大小。

我想象这是一种循环,通过x和y轴进行Bresenham线,在源图像和目标图像重叠的像素中进行某种混合 - 某种亚像素混合?

有哪些方法,如何混合像素?

是否有标准方法来解决这个问题?


1
threeblindmiceandamonkey.com已经消失,但是[https://github.com/marcantonio/imagekiln/blob/master/perspective.c]有一个quad_to_quad()函数。 - Camille Goudeseune
4个回答

20
你需要的是平面矫正,但恐怕并不简单。你需要完成的是恢复单应性变换,使得斜视的车侧图映射到正视的视角上。Photoshop/等软件可通过一些控制点为您完成此操作;如果您想自己实现,就必须开始深入了解计算机视觉。

编辑 - 好的,这里有一个Python脚本可以进行变形,使用OpenCV库,该库具有方便的函数来计算单应性和图像变形:

import cv

def warpImage(image, corners, target):
    mat = cv.CreateMat(3, 3, cv.CV_32F)
    cv.GetPerspectiveTransform(corners, target, mat)
    out = cv.CreateMat(height, width, cv.CV_8UC3)
    cv.WarpPerspective(image, out, mat, cv.CV_INTER_CUBIC)
    return out

if __name__ == '__main__':
    width, height = 400, 250
    corners = [(171,72),(331,93),(333,188),(177,210)]
    target = [(0,0),(width,0),(width,height),(0,height)]
    image = cv.LoadImageM('fries.jpg')
    out = warpImage(image, corners, target)
    cv.SaveImage('fries_warped.jpg', out)

输出结果如下:
扭曲的图像

OpenCV还具有C和C++绑定,或者你可以使用EmguCV进行.NET包装;API在所有语言中都是相当一致的,因此您可以在任何适合您的语言中复制此操作。


我担心我的问题表述太笼统了 - 我知道我要提取的矩形面的尺寸,以及它在图像中的角落坐标,让我想知道如何获得最佳质量的像素 - 如何计算每个像素的覆盖范围,以及如何最好地混合它们。 - Will
1
@Will - 在图像中选择四个四边形角点和四个对应的矩形点就足以恢复单应性(即变换矩阵),但你需要进行一些数学计算;请参考这个pdf文件:http://www.cs.brown.edu/courses/csci1950-g/asgn/proj6/resources/ProjectiveMappings.pdf - tzaman

5
请查阅“四点到四点”变换,例如threeblindmiceandamonkey。对于二维齐次坐标的3x3变换可以将任意四个点(一个四边形)转换为另一个四边形;反之,任何来自四边形和目标四边形的点都可以提供一个3 x 3变换,例如您卡车的角落和目标矩形的角落。
Qt拥有quadToQuad函数并可借此进行像素图转换,但是我猜您没有安装Qt?
添加于6月10日: 在labs.trolltech.com/page/Graphics/Examples中,有一个很好的演示程序,当您移动角落时,将像素图四点到四点变换。

alt text

添加于6月11日:@Will,这是Python中的translate.h(您懂一点吗?""" ..."""是多行注释)。perstrans()是关键;如果不理解,请问我。
顺便说一下,您可以逐个映射像素,mapQuadToQuad(目标矩形,原四边形),但是没有像素插值,它看起来会很糟糕;OpenCV可以实现全部功能。
#!/usr/bin/env python
""" square <-> quad maps
    from http://threeblindmiceandamonkey.com/?p=16 matrix.h
"""

from __future__ import division
import numpy as np

__date__ = "2010-06-11 jun denis"

def det2(a, b, c, d):
    return a*d - b*c

def mapSquareToQuad( quad ):  # [4][2]
    SQ = np.zeros((3,3))
    px = quad[0,0] - quad[1,0] + quad[2,0] - quad[3,0]
    py = quad[0,1] - quad[1,1] + quad[2,1] - quad[3,1]
    if abs(px) < 1e-10 and abs(py) < 1e-10:
        SQ[0,0] = quad[1,0] - quad[0,0]
        SQ[1,0] = quad[2,0] - quad[1,0]
        SQ[2,0] = quad[0,0]
        SQ[0,1] = quad[1,1] - quad[0,1]
        SQ[1,1] = quad[2,1] - quad[1,1]
        SQ[2,1] = quad[0,1]
        SQ[0,2] = 0.
        SQ[1,2] = 0.
        SQ[2,2] = 1.
        return SQ
    else:
        dx1 = quad[1,0] - quad[2,0]
        dx2 = quad[3,0] - quad[2,0]
        dy1 = quad[1,1] - quad[2,1]
        dy2 = quad[3,1] - quad[2,1]
        det = det2(dx1,dx2, dy1,dy2)
        if det == 0.:
            return None
        SQ[0,2] = det2(px,dx2, py,dy2) / det
        SQ[1,2] = det2(dx1,px, dy1,py) / det
        SQ[2,2] = 1.
        SQ[0,0] = quad[1,0] - quad[0,0] + SQ[0,2]*quad[1,0]
        SQ[1,0] = quad[3,0] - quad[0,0] + SQ[1,2]*quad[3,0]
        SQ[2,0] = quad[0,0]
        SQ[0,1] = quad[1,1] - quad[0,1] + SQ[0,2]*quad[1,1]
        SQ[1,1] = quad[3,1] - quad[0,1] + SQ[1,2]*quad[3,1]
        SQ[2,1] = quad[0,1]
        return SQ

#...............................................................................
def mapQuadToSquare( quad ):
    return np.linalg.inv( mapSquareToQuad( quad ))

def mapQuadToQuad( a, b ):
    return np.dot( mapQuadToSquare(a), mapSquareToQuad(b) )

def perstrans( X, t ):
    """ perspective transform X Nx2, t 3x3:
        [x0 y0 1] t = [a0 b0 w0] -> [a0/w0 b0/w0]
        [x1 y1 1] t = [a1 b1 w1] -> [a1/w1 b1/w1]
        ...
    """
    x1 = np.vstack(( X.T, np.ones(len(X)) ))
    y = np.dot( t.T, x1 )
    return (y[:-1] / y[-1]) .T

#...............................................................................
if __name__ == "__main__":
    np.set_printoptions( 2, threshold=100, suppress=True )  # .2f

    sq = np.array([[0,0], [1,0], [1,1], [0,1]])
    quad = np.array([[171, 72], [331, 93], [333, 188], [177, 210]])
    print "quad:", quad
    print "square to quad:", perstrans( sq, mapSquareToQuad(quad) )
    print "quad to square:", perstrans( quad, mapQuadToSquare(quad) )

    dw, dh = 300, 250
    rect = np.array([[0, 0], [dw, 0], [dw, dh], [0, dh]])
    quadquad = mapQuadToQuad( quad, rect )
    print "quad to quad transform:", quadquad
    print "quad to rect:", perstrans( quad, quadquad )
"""
quad: [[171  72]
 [331  93]
 [333 188]
 [177 210]]
square to quad: [[ 171.   72.]
 [ 331.   93.]
 [ 333.  188.]
 [ 177.  210.]]
quad to square: [[-0.  0.]
 [ 1.  0.]
 [ 1.  1.]
 [ 0.  1.]]
quad to quad transform: [[   1.29   -0.23   -0.  ]
 [  -0.06    1.79   -0.  ]
 [-217.24  -88.54    1.34]]
quad to rect: [[   0.    0.]
 [ 300.    0.]
 [ 300.  250.]
 [   0.  250.]]
"""

我正在努力,已经更新了问题,但感谢你提供的出色线索。 - Will
三只瞎老鼠和一只猴子的代码加上我的超采样;OpenCV中的双线性等参数让我感到不安。 - Will

0

我认为你需要的是仿射变换,可以通过矩阵运算来完成。


4
你说得对,我忘记了透视的因素。仿射变换会导致图像失真。抱歉。 - Andy West

0

而在现代,使用cv2的Python。

import cv2
import numpy as np

source_image = cv2.imread('french fries in Europe.jpeg')

source_corners = np.array([(171, 72), (331, 93), (333, 188), (177, 210)])

width, height = 400, 250
target_corners = np.array([(0, 0), (width, 0), (width, height), (0, height)])

# Get matrix H that maps source_corners to target_corners
H, _ = cv2.findHomography(source_corners, target_corners, params=None)

# Apply matrix H to source image.
transformed_image = cv2.warpPerspective(
    source_image, H, (source_image.shape[1], source_image.shape[0]))

cv2.imwrite('tranformed_image.jpg', transformed_image)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接