图像任你转,黑边不再现

图像旋转原理

想着疫情期间自己弄个项目练练手。说干就是干,这两天先是做xml格式的数据集(自己做真的是太麻烦了),由于数据量感觉不是很够,想通过图像增强来扩充数据量。在通过数据增强处理数据的时候,发现旋转挺好的,就是没人用,于是乎就有了这篇图像任你转,黑边不再现,的想法,通过艰难的三角函数推导,终于做出来了个半成品,哈哈哈。

点的旋转

在这里插入图片描述
点(x,y)通过顺时针旋转θ,变换为(x’,y’)。它是如何变换的呢?
这里用三角函数进行表示。先是x,y的由来
在这里插入图片描述
然后是,x’,y’的由来
在这里插入图片描述
这里想要用x,y来表示x’,y’,先要引入三角函数的展开式
在这里插入图片描述
根据展开式可以将x’,y’化简为下面的形式:
在这里插入图片描述
转换成我们熟悉的矩阵形式,
在这里插入图片描述
经过上面的矩阵相乘操作就可以得到了点旋转后的坐标了。

图像的旋转

图像可以说是由一个个点组成的,在电脑的世界里,它们认为图像有以下两点特征:
1. 图像是数字矩阵;
2. 图像的起始点是图像的左上角第一个元素。

在这里插入图片描述

就像上面这个图一样。
因此想要实现图像旋转得先进行坐标轴的平移。就像下面这样
在这里插入图片描述

这里W,H分别表示矩阵的宽和长。将(0,0)平移到图像的中心点,通过矩阵表示就是
在这里插入图片描述
当然,我们在计算完成后还得把它的坐标轴给平移回来,这样求一下它的逆运算就可以了
在这里插入图片描述
这样把上面的连起来,就是图像旋转的变换公式了
在这里插入图片描述

先来个代码爽一下:

import math
import numpy as np
import cv2

def img_rotate(image, angle):
    '''
    
    :param image: 输入图像
    :param angle: 旋转角度
    :return: 
    '''
    # 这里注意图像的形状是长和宽,而不是宽和长
    h, w, channels = image.shape
    # 角度转换
    anglePi = angle * math.pi / 180.0
    cosA = math.cos(anglePi)
    sinA = math.sin(anglePi)
    img_r = np.zeros((h, w, channels), dtype=np.uint8)
    # 创建坐标轴平移矩阵
    center_array = np.array([[1, 0, 0],
                             [0, -1, 0],
                             [-0.5*w, 0.5*h, 1]
                             ])
    # 创建坐标轴平移矩阵逆运算矩阵
    i_center_array= np.array([[1, 0, 0],
                             [0, -1, 0],
                             [0.5*w, 0.5*h, 1]
                             ])
    # 创建旋转矩阵
    rotate_array = np.array([[cosA, -sinA, 0],
                             [sinA, cosA, 0],
                             [0, 0, 1]
                             ])
    # 矩阵相乘,这里方便观看写成两个式子相乘,其实可以写成
    #i_center_array.dot(rotate_array.dot(center_array))
    rotate = np.dot(center_array, rotate_array)
    i_rotate = np.dot(rotate, i_center_array)

    for i in range(0, w):
        for j in range(0, h):
            # 这是矩阵乘完后的展开式,原理一样
            # x = int(cosA*i-sinA*j-0.5*w*cosA+0.5*h*sinA+0.5*w)
            # y = int(sinA*i+cosA*j-0.5*w*sinA-0.5*h*cosA+0.5*h)
            new = np.dot(np.array([i, j, 1]), i_rotate)
            # 坐标没有小数,必须取整
            x = int(new[0])
            y = int(new[1])
            # 防止溢出
            if x >= 0 and x < w and y >= 0 and y < h:
                img_r[j, i] = image[y, x]

    return img_r

image = cv2.imread("E:\Learn\dataset\dtd\images/bubbly/bubbly_0096.jpg")
image_r = img_rotate(image, 30)
cv2.imshow('rotate',image_r)
cv2.imshow('original',image)
cv2.waitKey()

在这里插入图片描述
在这里插入图片描述
可以看到,这里旋转后的图像被截取了四个角,并且还有黑边。因此,出现了图像旋转的改良版,原理就是对图像进行扩大,大小为旋转后四个角坐标中的最大值作为长和宽。这里就不放代码了,要不后面正主写不下了。但同样的问题是,这种办法还是有黑边,而且图像的尺寸改变了,这并不是我们想要的,特别是我们需要旋转图像进行数据增强时。那如何在不改变图像尺寸的同时还能去除黑边呢?

图像去黑

原理

这里要先来一张图,看一下几何上图像旋转是啥样的。
在这里插入图片描述
可以看出了,这里是逆时针旋转的,旋转角度为θ(但是我上边的代码是是按照顺时针旋转写的,结果跑完就成逆时针旋转了,也不知道咋回事。所以后面我就按照逆时针旋转来写如何去除黑色的边边角角。)
也就是将下图中的四个黑色的三角形给它弄没了。
在这里插入图片描述
具体如何实现呢?将图像看做一个矩形,求出原矩形ABCD和旋转后的矩形A’B’C’D’的交点,如下图
在这里插入图片描述
以其中一个三角形BEF为例,这里有两种思路,
(1)将三角形BEF扩充成一个矩形BEGF
在这里插入图片描述
这样可以得到三角形BEF和三角形GEF两个全等三角形。用三角形GEF内的像素值来替代三角形BEF内的黑色像素值。
这个方法有以下缺点:

  1. 对于部分前景和背景较为突出的图像会出现一定的错位现象;
  2. 当需要的特征在矩形的四个边角时,这个方法会打乱原有的特征;
  3. 图像在旋转后会出现边界。

(2)同样是对三角形进行扩充,不过这回是通过B’C’线段进行镜像扩充,如下图所示
在这里插入图片描述
这样同样可以得到三角形BEF和三角形GEF两个全等三角形。用三角形GEF内的像素值来替代三角形BEF内的黑色像素值。这个方法与(1)相比,旋转后的三角形图像具有D’C’方向的方向特征。但是它还是有以下缺点:

  1. 对于部分图像会出现一定的错位现象(与(1)相比好多了);
  2. 当需要的特征在矩形的四个边角时,这个方法会拉伸原有的特征;
  3. 图像在旋转后仍会出现边界。

实现

重点来了,这里给大家看看通过(1)方法进行图像变换的原理和效果,这里选取不同特征的图像进行测试。(至于方法(2)我还没写出来代码,哈哈哈)
得先求出来旋转后A’B’C’D’各个线段的表达式,然后求出交点的坐标,进而确定黑色三角形的面积。

    # 直线A'B':y = tanA*x + h/(2*cosA)
    # 直线B'C':y = -1/tanA*x + w/(2*sinA)
    # 直线C'D':y = tanA*x - h/(2*cosA)
    # 直线D'A':y = -1/tanA*x - w/(2*sinA)
    # 直线B'C'与 AB 交点
    x_BC_AB = int(-(h/2-w/(2*sinA))*tanA) ; y_BC_AB = h//2
    # 直线B'C'与 BC交点
    x_BC_BC = w//2;y_BC_BC = int(-w/(2*tanA)+w/(2*sinA))
    BC_AB = np.dot(np.array([x_BC_AB, y_BC_AB, 1]), i_center_array)
    BC_BC = np.dot(np.array([x_BC_BC, y_BC_BC, 1]), i_center_array)

    # 直线C'D'与 CD 交点
    x_CD_CD = int((-h/2+h/(2*cosA))/tanA) ; y_CD_CD = -h//2
    # 直线C'D'与 BC 交点
    x_CD_BC = w//2;y_CD_BC = int(w/2*tanA-h/(2*cosA))
    CD_CD = np.dot(np.array([x_CD_CD, y_CD_CD, 1]), i_center_array)
    CD_BC = np.dot(np.array([x_CD_BC, y_CD_BC, 1]), i_center_array)

    # 直线D'A'与 CD 交点
    x_DA_CD = int((h / 2 - w / (2 * sinA)) * tanA); y_DA_CD = -h // 2
    # 直线D'A'与 AD 交点
    x_DA_AD = -w // 2; y_DA_AD = int(w / (2 * tanA) - w / (2 * sinA))
    DA_CD = np.dot(np.array([x_DA_CD, y_DA_CD, 1]), i_center_array)
    DA_AD = np.dot(np.array([x_DA_AD, y_DA_AD, 1]), i_center_array)

    # 直线A'B'与 AB 交点
    x_AB_AB = int((h / 2 - h / (2 * cosA)) / tanA); y_AB_AB = h // 2
    # 直线A'B'与 AD 交点
    x_AB_AD = -w // 2; y_AB_AD = int(-w / 2 * tanA + h / (2 * cosA))
    AB_AB = np.dot(np.array([x_AB_AB, y_AB_AB, 1]), i_center_array)
    AB_AD = np.dot(np.array([x_AB_AD, y_AB_AD, 1]), i_center_array)

这里注意一点,黑色像素的数值为0,为了方便计算,这里直接相加就可以得到想要的
先来一张路飞的图片,哈哈哈。从左到右依次是原图,正常的旋转和去黑边的旋转
在这里插入图片描述
可以看出来,对于这种图像,效果并不是很好。

然后来一张气泡图
在这里插入图片描述
这个看着就能好多了,接着换一张,这个边界就很明显了
在这里插入图片描述
接下来,用一张织物的图像来试一下。
在这里插入图片描述
可以看出来效果还是可以的,接下来就是鼓动如何去除边界了。

欲知后事如何,弄完了

经过本人不断地研究,想了不同的方法,经过验证结果效果都不如这个,这个方法呢出现黑边或者亮边是由于像素的叠加导致的,因此在图像区域ROI的时候,减个1就能得到很大的改善。

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
在Python中使用OpenCV库进行图像时,有时候会出现边的情况。为了避免边的出现,可以采用以下几种方法来处理: 1. 填充颜色:在旋图像之前,可以先将图像的边缘进行填充,使其大小能够容纳旋后的图像。填充的颜色可以根据需要选择,一般可以选择与图像背景相近的颜色,比如白色(255,255,255)。 2. 裁剪图像:旋图像后,可以根据需要裁剪掉边部分,只保留旋后的有效图像区域。裁剪的方法可以根据具体情况选择,比如根据旋图像的大小来裁剪或者根据预设的目标图像大小来裁剪。 3. 调整旋中心:在使用cv.getRotationMatrix2D函数时,可以调整旋中心的位置,使其位于图像的中心位置,这样可以减少旋图像边缘出现边的可能性。 综上所述,通过填充颜色、裁剪图像或调整旋中心的方式,可以在Python中实现图像时避免边的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Python+OpenCV 实现图片无损旋90°且无边](https://download.csdn.net/download/weixin_38668225/12857214)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [OpenCV Python图像,0-360度随机旋,同时去除边](https://blog.csdn.net/qq_40037127/article/details/125206182)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值