数字图像处理个人练习01--仿射变换

本文档详细介绍了如何使用numpy库和自定义函数实现图像的平移、旋转、缩放和斜切操作。作者通过创建radial_trans类,封装了这些图形变换方法,并与opencv库函数进行了效果对比。测试结果显示,自定义函数能够实现基本的图像变换,但可能在图像边缘处理和插值算法上与库函数存在差异。
摘要由CSDN通过智能技术生成

环境配置

本次项目使用

numpy库

基于C++开发提供python接口的cv2库

python自带的math库进行数学计算

思路

\quad 通过对opencv库中的图形变换函数,自己定义了一个radial_trans类来对图片矩阵实现平移、旋转、缩放、斜切来进行封装,最后通过main函数接口来对每个封装的函数进行模块化测试

仿射变换

1.平移

函数形式

def translation(image = np.full((1,1,3), (255, 255, 0)), dwidth = 0, dheight = 0, change_pic_w = 0, change_pic_h = 0)

该函数具有5个参数

imgae为图像矩阵
dwidth和dheight分别代表图像向右和向下平移的距离
而后续的两个参数表示最终显示的图像大小

由于平移的过程中可能会出现一些没有数据的空白点,这里使用天蓝色对空白点进行填充以区别系统自带的函数。

核心代码

for i in range(width):
            if (i + dwidth < 0) or (i + dwidth >= width + change_pic_w):
                continue
            else:
                for j in range(height):
                    if (j + dheight < 0) or (j + dheight >= height + change_pic_h):
                        continue
                    else:
                        new_mat[j + dheight][i + dwidth] = image[j][i]

本质上就是一个个点的复制

测试效果

自己的效果
自己的效果

库函数效果
库函数效果

2.旋转

函数说明

def rotate(image, angle, change_pic_w, change_pic_h):

对比平移函数,区别只有angle,这里只需要输入角度即可,函数内会转换成弧度制

核心代码

        rot_max = np.matrix([[cos(a), sin(a), 0], 
                            [-sin(a), cos(a), 0],
                            [0,     0,      0]])

        for x in range(width+change_pic_w):
            for y in range(height+change_pic_h):
                point = np.matrix([x , y , 1])


                list = np.matmul(point, rot_max).tolist()

                new_x = int(floor(list[0][0]))
                new_y = int(floor(list[0][1]))


                u = list[0][0] - new_x
                v = list[0][1] - new_y
                #线性插值的变量

                if new_x < 0 or new_y < 0\
                or new_y >= height  \
                or new_x >= width :
                    continue
                #判定是否在边界内

                if (new_x + 1 >= width
                    or new_y + 1 >= height):
                    new_mat[y][x] = image[new_y][new_x]

                else:
                    new_mat[y][x] = \
                                    (1-u)*(1-v)*image[new_y][new_x] + \
                                   (1-u)*v*image[new_y][new_x+1]+ \
                                   u*(1-v)*image[new_y+1][new_x] +\
                                   u*v*image[new_y+1][new_x+1]
        return np.uint8(new_mat)

创建好矩阵后先对每一个原图像点进行矩阵的运算,然后通过逆运算进行反向的计算(把要目标矩阵图形一个个点逆映射到原来图像的点)最后通过二分插值运算来对离散映射过程损失的点进行拟合

测试效果

在这里插入图片描述
自己的函数

在这里插入图片描述
库函数

3.缩放

函数说明

scale(image, x_times, y_times)

x_times,y_times分别代表在宽度和高度的变化比例

核心代码

        for y in range(new_y):
            for x in range(new_x):
                #求目标点在原图的位置
                temp_x = (x + 0.5)* (width/new_x)-0.5
                temp_y = (y + 0.5)* (height/new_y)-0.5
                if temp_x < 0:
                    temp_x = 0
                elif temp_x >= width:
                    temp_x = width - 1
                if temp_y < 0:
                    temp_y = 0
                elif temp_y >= height:
                    temp_y = height - 1
                
                # 确定最近的四个点
                x1 = int(np.floor(temp_x))
                y1 = int(np.floor(temp_y))
                x2 = int(min(x1+1, width-1)) #防止超出原图像范围
                y2 = int(min(y1+1, height-1))
                dx = x2 - x1 if(x2 - x1) else 1
                dy = y2 - y1 if(y2 - y1) else 1

                for i in range(3):
                    r1 = ((x2 - temp_x) * image[y1][x1][i] + (temp_x - x1)*image[y1][x2][i]) / (dx)
                    r2 = ((x2 - temp_x) * image[y2][x1][i] + (temp_x - x1)*image[y2][x2][i]) / (dx)
                    
                    r3 = ((y2 - temp_y) * r1 + (temp_y - y1) * r2) / (dy)
                    new_mat[y, x, i] = int(r3)      

形式上也是类似旋转,最后都是使用了双线性插值法避免图像的失真

测试效果

在这里插入图片描述
左上为自己实现,右上为库函数
下方为原图

在这里插入图片描述
放大的情况

4.斜切

函数说明

def slant(image, alpha_x, alpha_y):

alpha_x和alpha_y为斜切变化的角度,直接输入角度大小即可

核心代码

sla_mat = np.array([
            [1, a_x, 0],
            [a_y, 1, 0],
            [0, 0, 1]
        ])
        height, width = image.shape[:2]
        new_mat = np.full((height, width, 3), (255,255,0))
        #创建黑色矩阵
        for y in range(height):
            for x in range(width):
                point = np.matrix([x , y , 1])
                list = np.matmul(point, sla_mat).tolist()
                new_x = int(floor(list[0][0]))
                new_y = int(floor(list[0][1]))
                u = list[0][0] - new_x
                v = list[0][1] - new_y
                #线性插值的变量

                if new_x < 0 or new_y < 0\
                or new_y >= height  \
                or new_x >= width :
                    continue
                #判定是否在边界内

                if (new_x + 1 >= width
                    or new_y + 1 >= height):
                    new_mat[y][x] = image[new_y][new_x]

                else:
                    new_mat[y][x] = \
                                    (1-u)*(1-v)*image[new_y][new_x] + \
                                   (1-u)*v*image[new_y][new_x+1]+ \
                                   u*(1-v)*image[new_y+1][new_x] +\
                                   u*v*image[new_y+1][new_x+1]

内容都是类似的,只是矩阵变化不一样

测试效果

在这里插入图片描述

最终代码


import numpy as np
from math import sin, cos, pi, floor
import cv2

class radial_trans:
    def translation(image = np.full((1,1,3), (255, 255, 0)), dwidth = 0, dheight = 0, change_pic_w = 0, change_pic_h = 0):
    #1图形矩阵  2向右平移   3向下平移   4图形显示宽度的改变 5图形显示高度的改变        
        shape = image.shape
        height = shape[0]
        width = shape[1]
        print(height,width)
        new_mat = np.full((height + change_pic_h, width + change_pic_w, 3), (255,255,0))
        #创建一个全为黑的窗口

        for i in range(width):
            if (i + dwidth < 0) or (i + dwidth >= width + change_pic_w):
                continue
            else:
                for j in range(height):
                    if (j + dheight < 0) or (j + dheight >= height + change_pic_h):
                        continue
                    else:
                        new_mat[j + dheight][i + dwidth] = image[j][i]
        new_mat = np.uint8(new_mat)
        return new_mat 
        
    def rotate(image, angle, change_pic_w, change_pic_h):
            #对比库函数增加了改变显示图像UI的大小操作
        height, width = image.shape[:2]
        new_mat = np.full((height + change_pic_h, width + change_pic_w, 3), (255,255,0))
        #创建黑色矩阵
        a = angle/180*pi

        # matrix1 = np.array([[1, 0, 0],
        #                 [0, -1, 0],
        #                 [-0.5 * height, 0.5 * width, 1]])

        rot_max = np.matrix([[cos(a), sin(a), 0], 
                            [-sin(a), cos(a), 0],
                            [0,     0,      0]])

        # matrix2 = np.array([[1, 0, 0],
        #                 [0, -1, 0],
        #                 [0.5 * height, 0.5 * width, 1]])

        for x in range(width+change_pic_w):
            for y in range(height+change_pic_h):
                point = np.matrix([x , y , 1])

                '''网络上找到的其他变化方法'''
                # temp1 = np.matmul(point, matrix1)
                # temp2 = np.matmul(temp1, rot_max)
                # list = np.matmul(temp2, matrix2).tolist()

                list = np.matmul(point, rot_max).tolist()

                new_x = int(floor(list[0][0]))
                new_y = int(floor(list[0][1]))


                u = list[0][0] - new_x
                v = list[0][1] - new_y
                #线性插值的变量

                if new_x < 0 or new_y < 0\
                or new_y >= height  \
                or new_x >= width :
                    continue
                #判定是否在边界内

                if (new_x + 1 >= width
                    or new_y + 1 >= height):
                    new_mat[y][x] = image[new_y][new_x]

                else:
                    new_mat[y][x] = \
                                    (1-u)*(1-v)*image[new_y][new_x] + \
                                   (1-u)*v*image[new_y][new_x+1]+ \
                                   u*(1-v)*image[new_y+1][new_x] +\
                                   u*v*image[new_y+1][new_x+1]
        return np.uint8(new_mat)


    def scale(image, x_times, y_times):

        height,width = image.shape[:2]
        new_y = int(height * y_times)
        new_x = int(width * x_times)
        new_mat = np.full((new_y, new_x, 3), (255,255,0))
        #创建黑色矩阵
        

        
        for y in range(new_y):
            for x in range(new_x):
                #求目标点在原图的位置
                temp_x = (x + 0.5)* (width/new_x)-0.5
                temp_y = (y + 0.5)* (height/new_y)-0.5
                if temp_x < 0:
                    temp_x = 0
                elif temp_x >= width:
                    temp_x = width - 1
                if temp_y < 0:
                    temp_y = 0
                elif temp_y >= height:
                    temp_y = height - 1
                
                # 确定最近的四个点
                x1 = int(np.floor(temp_x))
                y1 = int(np.floor(temp_y))
                x2 = int(min(x1+1, width-1)) #防止超出原图像范围
                y2 = int(min(y1+1, height-1))
                dx = x2 - x1 if(x2 - x1) else 1
                dy = y2 - y1 if(y2 - y1) else 1

                for i in range(3):
                    r1 = ((x2 - temp_x) * image[y1][x1][i] + (temp_x - x1)*image[y1][x2][i]) / (dx)
                    r2 = ((x2 - temp_x) * image[y2][x1][i] + (temp_x - x1)*image[y2][x2][i]) / (dx)
                    
                    r3 = ((y2 - temp_y) * r1 + (temp_y - y1) * r2) / (dy)
                    new_mat[y, x, i] = int(r3)

        return np.uint8(new_mat)

    def slant(image, alpha_x, alpha_y):
        a_x = alpha_x / 180 * pi
        a_y = alpha_y / 180 * pi
        sla_mat = np.array([
            [1, a_x, 0],
            [a_y, 1, 0],
            [0, 0, 1]
        ])
        height, width = image.shape[:2]
        new_mat = np.full((height, width, 3), (255,255,0))
        #创建黑色矩阵
        for y in range(height):
            for x in range(width):
                point = np.matrix([x , y , 1])
                list = np.matmul(point, sla_mat).tolist()
                new_x = int(floor(list[0][0]))
                new_y = int(floor(list[0][1]))
                u = list[0][0] - new_x
                v = list[0][1] - new_y
                #线性插值的变量

                if new_x < 0 or new_y < 0\
                or new_y >= height  \
                or new_x >= width :
                    continue
                #判定是否在边界内

                if (new_x + 1 >= width
                    or new_y + 1 >= height):
                    new_mat[y][x] = image[new_y][new_x]

                else:
                    new_mat[y][x] = \
                                    (1-u)*(1-v)*image[new_y][new_x] + \
                                   (1-u)*v*image[new_y][new_x+1]+ \
                                   u*(1-v)*image[new_y+1][new_x] +\
                                   u*v*image[new_y+1][new_x+1]

        return np.uint8(new_mat)

#平移
def test_trans(image):
    translation_matrix = np.float32([[1, 0, 20],
                                    [0,1,50]])
    dst=cv2.warpAffine(image, translation_matrix, (width+20,height+50))

    my_trans = radial_trans.translation(image, 20, 50, 20,50)
    cv2.imshow("",my_trans)
    cv2.imshow('dst',dst)
    cv2.waitKey(0)

def test_rot(image):
    # 旋转
    rot_mat = cv2.getRotationMatrix2D( (0,0), 15, 1)
    #库函数,旋转点, 角度, 放缩大小
    
    #参数1 旋转中心坐标 参数2 旋转角度 参数3 缩放比例
    dst = cv2.warpAffine(image, rot_mat, (width, height))
    my_rot = radial_trans.rotate(image, 15, 100, 0)
    cv2.imshow('dst',dst)
    cv2.imshow("",my_rot)
    cv2.waitKey(0)

def test_sca(image):
    dstx = int(image.shape[1]*0.5)
    dsty = int(image.shape[0]*0.5)

    my_sca = radial_trans.scale(image, 1.5, 1.1)
    dst = cv2.resize(image, (dstx,dsty))

    cv2.imshow('dst',dst)
    cv2.imshow("my_sca",my_sca)
    cv2.imshow('',image)
    cv2.waitKey(0)

def test_sla(image):
    dx = -15
    dy = -30

    height, width = image.shape[:2]
    p1 = np.float32([[0,0], [width-1,0], [0,height-1]])
    p2 = np.float32([[0,0], [width *0.3,height], [width,height]])
    M = cv2.getAffineTransform(p1, p2)
    dst = cv2.warpAffine(image, M, (width, height))

    my_sla = radial_trans.slant(image, dx, dy)
    cv2.imshow("my_sla",my_sla)
    cv2.imshow("dst", dst)
    cv2.waitKey(0)

if __name__ == '__main__':
    image = cv2.imread("pic1.png")
    print(type(image))
    height, width = image.shape[:2]
    #test_trans(image)
    #test_rot(image)
    #test_sca(image)
    test_sla(image)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值