在图像处理中,经常需要对图像内的一些轮廓进行特殊处理,这时候可能需要使用坐标转换功能。例如下面的轮廓中,在找到最小外接矩形后,就需要对轮廓已最小外接矩形的坐标进行处理。

java opencv 坐标 opencv坐标转换_顺时针

在一幅图像的坐标系中,通常将左上角坐标作为原点(0,0),水平向右和垂直向下的方向分别为x轴和y轴的正方向,称为 XY 坐标系。现以最小 外 接 矩 的 一 个 顶 点 为 坐 标 原 点,
两条垂直邻边为坐 标 轴 建 立 新 坐 标 系,称为 AB坐标系。两个坐标系平移和旋转的变化关系如图所示:

java opencv 坐标 opencv坐标转换_java opencv 坐标_02

如上图所示,在 AB坐标系内的(A,B)点在 XY 坐标系内设为(X,Y)点,坐标的转换关系式如下:
  X=Acosθ-Bsinθ+a                          (1)
  Y=Asinθ+Bcosθ+b 
其逆转换的公式为:
A=(X-a)cosθ+(Y-b)sinθ           (2)
B=-(X-a)sinθ+(Y-b)cosθ 
首先,求出矩形在 XY 坐标系的4个顶点值。记矩形的左上、左下、右下、右上顶点分别为:A1,A2,A3,A4,选择其中一个合适的点如 A1=(a,b)作为新坐标系的原点,按照式(2)转换到 AB系内,转换后 A1=(0,0),A2=(0,N),A3=(M,N),A4=(M,0)(注:M,N 分别代表由 XY 坐标系中 A2的y值、A4的x 值转化到 AB坐标系中得到的常数值)

 

在python OpenCV中,通过cv2.minAreaRect()函数求得输入轮廓的最小外接矩形,返回三个参数,分别是最小外接矩形的质心,(宽,高),旋转角度。

旋转角度P是水平轴(x轴)逆时针旋转,与矩形相碰的第一条边的夹角,并指定这条边是width,另一条边边长是height。也就是说,width与height不是按照长短来定义的。
在opencv中,坐标系原点在左上角,相对于x轴,逆时针旋转角度为负,顺时针旋转角度为正。在这里,P∈(-90度,0]

cv2.boxPoints()可获取最小外接矩形的四个顶点坐标,顶点坐标序号是XY坐标系中纵坐标最大值为box[0],然后顺时针依次是box[1],box[2],box[3]

java opencv 坐标 opencv坐标转换_java opencv 坐标_03

 

坐标转换例子:下面的例子将最小外接轮廓包围的轮廓的坐标转换为以最小外接矩形为坐标的坐标。注,按照资料,这里的外接矩形的旋转角度应该是上面坐标转换公式中的P角度,既θ=90-p

java opencv 坐标 opencv坐标转换_旋转角度_04

java opencv 坐标 opencv坐标转换_旋转角度_05

代码实现:

'''
坐标转换换算
'''
def coordinateTransform(coordinateXY,zeroAB,angle):
    A = (coordinateXY[0]-zeroAB[0]) * np.cos(angle) + (coordinateXY[1]-zeroAB[1]) * np.sin(angle)
    B = (coordinateXY[1]-zeroAB[1]) * np.cos(angle) -(coordinateXY[0]-zeroAB[0]) * np.sin(angle)
    return [A,B]


if __name__ == "__main__":
    imagePath = "F:/Semantic_Segmentation_Keras-master/workspace\preds\Shujuku_1500/1/5.png"
    img = cv2.imread(imagePath)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 155, 255, cv2.THRESH_BINARY)
    contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for c in range(len(contours)):
        area = cv2.contourArea(contours[c])
        #中心点,长宽,旋转角度
        rect = cv2.minAreaRect(contours[c])
        print("minAreaRect width",rect[1][0])
        print("minAreaRect heigth", rect[1][1])
        boxes = cv2.boxPoints(rect)
        boxes = np.int0(boxes)

        print(coordinateTransform(boxes[0],boxes[2], 90-np.abs(rect[2])))
        print(coordinateTransform(boxes[1], boxes[2], 90-np.abs(rect[2])))
        print(coordinateTransform(boxes[2], boxes[2], 90-np.abs(rect[2])))
        print(coordinateTransform(boxes[3], boxes[2], 90-np.abs(rect[2])))

        cv2.drawContours(img,[boxes],-1,(0,0,255),1)
    cv2.imshow("image",img)
    cv2.waitKey(0)

结果输输出:

minAreaRect width 139.6551971435547
minAreaRect heigth 208.635009765625
[128.2447642990981, -215.60445364108037]
[138.73613986716262, -7.299554421955371]
[0.0, -0.0]
[-10.491375568064527, -208.30489921912496]

发现结果不对,

第一,坐标方向不对

第二,坐标对的情况下,误差怎样消除?结果中的-7.299554421955371 ,-10.491375568064527 都应该是0的

 

有了解如何解决这方面问题的童鞋吗?