opencv 在旋转、缩放和平移(使用Python变换矩阵)后裁剪图像,使其没有黑色背景

sgtfey8w  于 2022-11-24  发布在  Python
关注(0)|答案(2)|浏览(228)

我有两对相同的2D物体的图像,它们的差别很小,一对图像中的两个图像有两个参考点(一个星星[x_s,y_s]和一个箭头[x_a,y_a]),如下所示:

我已经编写了一个Python脚本来将一个图像与参考点/坐标对中的第二个图像对齐。请浏览下面的代码以获得清晰的理解:

import numpy as np
import cv2
import pandas as pd

# Function to align image2 with respect to image1:

def alignFromReferenceImage(image1, imgname1, image2, imgname2):
    
    # Using Panda dataframe to read the coordinate values ((x_s,y_s) and (x_a,y_a)) from a csv file
    #
    # The .csv file looks like this:-
    #
    #     id;x_s;y_s;x_a;y_a
    #     img11;113;433;45;56
    #     img12;54;245;55;77
    #     img21;33;76;16;88
    #     img22;62;88;111;312
    #     ...  ;..;..;...;  

    df = pd.read_csv("./image_metadata.csv",  delimiter= ';')

    # Eliminate .jpg from the image name and fetch the row

    filter_data=df[df.isin([imgname1.split('.')[0]]).any(1)]  
    
    x1_s=filter_data['x_s'].values[0]
    y1_s=filter_data['y_s'].values[0]
    
    x1_a=filter_data['x_a'].values[0]
    y1_a=filter_data['y_a'].values[0]

    filter_data2=df[df.isin([imgname2.split('.')[0]]).any(1)]
    
    x2_s=filter_data2['x_s'].values[0]
    y2_s=filter_data2['y_s'].values[0]
    
    x2_a=filter_data2['x_a'].values[0]
    y2_a=filter_data2['y_a'].values[0]
    
    tx=x2_s-x1_s
    ty=y2_s-y1_s
    
    rows,cols = image1.shape
    M = np.float32([[1,0,-tx],[0,1,-ty]])
    image_after_translation = cv2.warpAffine(image2,M,(cols,rows))
    
    d1 = math.sqrt((x1_a - x1_s)**2 + (y1_a - y1_s)**2)
    d2 = math.sqrt((x2_a - x2_s)**2 + (y2_a - y2_s)**2)
    
    dx1 = x1_a - x1_s
    dy1 = -(y1_a - y1_s)
    
    alpha1 = math.degrees(math.atan2(dy1, dx1))
    alpha1=(360+alpha1) if (alpha1<0) else alpha1
    
    dx2 = x2_a - x2_s
    dy2 = -(y2_a - y2_s)

    alpha2 = math.degrees(math.atan2(dy2, dx2))
    alpha2=(360+alpha2) if (alpha2<0) else alpha2
    
    ang=alpha1-alpha2
    
    scale = d1 / d2 
    
    centre = (filter_data['x_s'].values[0], filter_data['y_s'].values[0])
    
    M = cv2.getRotationMatrix2D((centre),ang,scale)
    aligned_image = cv2.warpAffine(image_after_translation, M, (cols,rows))

    return aligned_image

对齐后,图像如下所示:

**重要提示:**现在,在将第一张图像与第二张图像对齐后,我想裁剪对齐的图像,使图像在裁剪后不再有黑色背景。下图将清楚地说明我想做什么:

我研究了一下,发现了一些有用的链接:

  1. http://roffle-largest-rectangle.blogspot.com/2011/09/find-largest-rectangle-in-rotated-image.html
  2. Rotate image and crop out black borders
  3. Calculate largest inscribed rectangle in a rotated rectangle
    但是这些帖子只讨论了旋转,我不知道平移和缩放的数学原理。在这个问题上的任何帮助都将非常感谢。
hkmswyz6

hkmswyz61#

如果你想要“任何帮助”,并愿意使用Imagemagick 7,那么有一个简单的解决方案使用其积极的修剪。
输入:

magick -fuzz 20% img.png +repage -bordercolor black -border 2 -background black -define trim:percent-background=0% -trim +repage img_trim.png

wj8zmpe1

wj8zmpe12#

下面是一个Python/OpenCV的解决方案。它首先对图像进行阈值处理,使背景为黑色,其余部分为白色。它测试阈值图像的每条边,计算平均值,并寻找平均值最低的边。如果平均值==255,它就停在那条边上。如果不是,它就修剪掉那条边并重复。一旦所有边的平均值都是255。它完全停止并使用每一侧上的增量来计算原始输入的裁剪。
输入:

  • 注意:我不得不调整您发布的图片的裁剪,以确保所有边的背景都是纯黑色的。如果您提供单独的图片会有帮助。如果边仍然是轻微的灰色,那么我会增加上限。*
import cv2
import numpy as np

# read image
img = cv2.imread('star_arrow.png')
h, w = img.shape[:2]

# threshold so border is black and rest is white. Note this is has pure black for the background, so threshold at black and invert. Adjust lower and upper if the background is not pure black.
lower = (0,0,0)
upper = (0,0,0)
mask = cv2.inRange(img, lower, upper)
mask = 255 - mask

# define top and left starting coordinates and starting width and height
top = 0
left = 0
bottom = h
right = w

# compute the mean of each side of the image and its stop test
mean_top = np.mean( mask[top:top+1, left:right] )
mean_left = np.mean( mask[top:bottom, left:left+1] )
mean_bottom = np.mean( mask[bottom-1:bottom, left:right] )
mean_right = np.mean( mask[top:bottom, right-1:right] )

mean_minimum = min(mean_top, mean_left, mean_bottom, mean_right)

top_test = "stop" if (mean_top == 255) else "go"
left_test = "stop" if (mean_left == 255) else "go"
bottom_test = "stop" if (mean_bottom == 255) else "go"
right_test = "stop" if (mean_right == 255) else "go"

# iterate to compute new side coordinates if mean of given side is not 255 (all white) and it is the current darkest side
while top_test == "go" or left_test == "go" or right_test == "go" or bottom_test == "go":

    # top processing
    if top_test == "go":
        if mean_top != 255:
            if mean_top == mean_minimum:
                top += 1
                mean_top = np.mean( mask[top:top+1, left:right] )
                mean_left = np.mean( mask[top:bottom, left:left+1] )
                mean_bottom = np.mean( mask[bottom-1:bottom, left:right] )
                mean_right = np.mean( mask[top:bottom, right-1:right] )
                mean_minimum = min(mean_top, mean_left, mean_right, mean_bottom)
                #print("top",mean_top)
                continue
        else:
            top_test = "stop"   

    # left processing
    if left_test == "go":
        if mean_left != 255:
            if mean_left == mean_minimum:
                left += 1
                mean_top = np.mean( mask[top:top+1, left:right] )
                mean_left = np.mean( mask[top:bottom, left:left+1] )
                mean_bottom = np.mean( mask[bottom-1:bottom, left:right] )
                mean_right = np.mean( mask[top:bottom, right-1:right] )
                mean_minimum = min(mean_top, mean_left, mean_right, mean_bottom)
                #print("left",mean_left)
                continue
        else:
            left_test = "stop"  

    # bottom processing
    if bottom_test == "go":
        if mean_bottom != 255:
            if mean_bottom == mean_minimum:
                bottom -= 1
                mean_top = np.mean( mask[top:top+1, left:right] )
                mean_left = np.mean( mask[top:bottom, left:left+1] )
                mean_bottom = np.mean( mask[bottom-1:bottom, left:right] )
                mean_right = np.mean( mask[top:bottom, right-1:right] )
                mean_minimum = min(mean_top, mean_left, mean_right, mean_bottom)
                #print("bottom",mean_bottom)
                continue
        else:
            bottom_test = "stop"    

    # right processing
    if right_test == "go":
        if mean_right != 255:
            if mean_right == mean_minimum:
                right -= 1
                mean_top = np.mean( mask[top:top+1, left:right] )
                mean_left = np.mean( mask[top:bottom, left:left+1] )
                mean_bottom = np.mean( mask[bottom-1:bottom, left:right] )
                mean_right = np.mean( mask[top:bottom, right-1:right] )
                mean_minimum = min(mean_top, mean_left, mean_right, mean_bottom)
                #print("right",mean_right)
                continue
        else:
            right_test = "stop" 

# crop input
result = img[top:bottom, left:right]

# print crop values 
print("top: ",top)
print("bottom: ",bottom)
print("left: ",left)
print("right: ",right)
print("height:",result.shape[0])
print("width:",result.shape[1])

# save cropped image
#cv2.imwrite('border_image1_cropped.png',result)
cv2.imwrite('img_cropped.png',result)
cv2.imwrite('img_mask.png',mask)

# show the images
cv2.imshow("mask", mask)
cv2.imshow("cropped", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

阈值图像:

裁剪的输入:

相关问题