数字图像处理-Python实现BMP图片(位图)旋转-代码实例

正确实例

第一次写博客,记录自己的作业!禁止照搬照抄!

前面的读BMP位图和显示图片信息的函数是我自己写的,不过参考链接找不到了。
编译器:Spyder(Python3.7)、pycharm

原图:
在这里插入图片描述

代码片

主要算法就是利用极坐标的旋转公式(可以查看我在最后贴的参考链接),遍历新图的每个坐标,计算对应原图的坐标,再将对应的像素值复制到新图片像素矩阵中。
前面的部分需要有对BMP位图基础知识的了解。

函数代码:

#图片绕几何中心逆时针旋转angle度
def rotate(img, angle):
    H, W, C = img.shape

    anglePi = angle * math.pi / 180.0
    cosA = math.cos(anglePi)
    sinA = math.sin(anglePi)
    
    #计算四个顶点旋转后的值,最大差就是新图像的长和宽
    vx,vy=[],[]
    for a in (0.5*H,-0.5*H):
        for b in (0.5*W,-0.5*W):
            vx.append(a*cosA+b*sinA)
            vy.append(-1*a*sinA+b*cosA)
    new_height = math.floor(max(vx)-min(vx))+1
    new_width = math.floor(max(vy)-min(vy))+1    
    #下面是原博主求新长和宽的方法,我试验后发现当旋转角度大于90度时会
    #出现不适配,感兴趣的同学可以试试
    #new_height = math.ceil(H * np.cos(anglePi) + W * np.sin(anglePi))
    #new_width = math.ceil(W * np.cos(anglePi) + H * np.sin(anglePi))

    out = np.zeros((new_height+1, new_width+1, C), dtype=int) 
	#坐标偏移量
    dx_back = 0.5 * W - 0.5 * new_width * cosA - 0.5 * new_height * sinA
    dy_back = 0.5 * H + 0.5 * new_width * sinA - 0.5 * new_height * cosA
    for y in range(new_height):
        for x in range(new_width):
            x0 =int( x * cosA + y * sinA + dx_back)
            y0 = int(y * cosA - x * sinA + dy_back)
            # 计算结果是这一范围内的x0,y0才是原始图像的坐标
            if 0 < x0 < W and 0 < y0 < H:  
                out[y, x] = img[y0, x0]  
    return out

附上主函数和会用到的其他函数:

import numpy as np
import struct
import matplotlib.pyplot as plt
import math
import operator
from operator import itemgetter

#设置图片参数为全局变量
f_size=0
f_ofset=0
f_wide=0
f_height=0
f_bitcount=0

def readInfo(f):
    ##读取文件头和信息头
    f_type = str(f.read(2)) ##开头两字节为文件类型  
    file_size_byte = f.read(4) ##3-6字节是文件长度
    f.seek(f.tell()+4) ##跳过四字节
    file_ofset_byte = f.read(4)##偏移量
    f.seek(f.tell()+4) ##跳过信息头长度
    file_wide_byte = f.read(4) ##宽度
    file_height_byte = f.read(4) ##高度
    f.seek(f.tell()+2) ##跳过位平面数
    file_bitcount_byte = f.read(4) ##每个像素占位
    
    #将读取的字节转换成指定类型
    global f_size,f_ofset,f_wide,f_height,f_bitcount
    f_size,=struct.unpack('l',file_size_byte)#字节字符串转化为long
    f_ofset,=struct.unpack('l',file_ofset_byte)
    f_wide,=struct.unpack('l',file_wide_byte)
    f_height,=struct.unpack('l',file_height_byte)
    f_bitcount,=struct.unpack('i',file_bitcount_byte)#转为int
    print("类型:",f_type,"\n大小:",f_size,"\n位图数据偏移量:",f_ofset,"\n宽度:",f_wide,"\n高度:",f_height,"\n位图:",f_bitcount)

def readImg(f):
    f.seek(54)
    #读取调色板
    color_table=np.empty(shape=[256,4],dtype=int)   
    for i in range(0,256):
        b = struct.unpack('B',f.read(1))[0]#integer
        g = struct.unpack('B',f.read(1))[0]
        r = struct.unpack('B',f.read(1))[0]
        alpha = struct.unpack('B',f.read(1))[0]
        color_table[i][0] = r
        color_table[i][1] = g
        color_table[i][2] = b
        color_table[i][3] = 255
        
    #读取数据部分
    f.seek(f_ofset)#指针偏移  
    #256色图像是八位伪彩色图像,一个像素占一个字节
    img = np.empty(shape=[f_height,f_wide,4],dtype=int)
    cout = 0
    for y in range (0,f_height):
        for x in range(0,f_wide):
            cout = cout+1;
            index = struct.unpack('B',f.read(1))[0]
            img[f_height-y-1,x]=color_table[index]
        #行像素不是4的倍数要将填充的字节读取掉
        while cout %4 !=0:
            #print(cout)
            f.read(1)
            cout = cout+1
    return img 

def showImg(img):
    #显示图像
    plt.imshow(img)
    plt.show()
    
if __name__=='__main__':
    filename = 'D:\\数字图像处理\\彩色lena图像256色.BMP'
    f=open(filename,'rb')
    readInfo(f)
    initImage = readImg(f)
    iSRotate = rotate(initImage,30)
    showImg(iSRotate)
    f.close()

运行结果

在这里插入图片描述

有问题的代码

这一部分代码是我听了老师讲课之后根据自己的想法写出来的,中间也花了很多心思,最后空穴的问题看有没有大佬能帮忙完善。但是最佳方法肯定还是根据新图找原图对应点,这种做法只是提供一种思路。

def rotate(image, angle):
    # 使用矩阵运算
    x = []
    y = []
    for i in range(f_height):
        for j in range(f_wide):
            anglePi = angle * math.pi / 180.0
            x.append(int(math.cos(anglePi) * i - math.sin(anglePi) * j + 0.5))
            y.append(int(math.sin(anglePi) * i + math.cos(anglePi) * j + 0.5))
    minX = min(x)
    maxX = max(x)
    minY = min(y)
    maxY = max(y)
    # 确定新图大小
    height = maxX - minX + 1
    width = maxY - minY + 1
    iSRotate = np.empty(shape=[height, width, 4], dtype=int)
    for i in range(f_height):
        for j in range(f_wide):
            iSRotate[x[i * f_wide + j] - minX, y[i * f_wide + j] - minY] = image[i, j]
    #以下是我尝试进行的改进但是没有成功qaq
    '''#填充空穴,均值插值法
    x3 = math.cos(anglePi)*(f_wide-1)-math.sin(anglePi)*(f_height-1)-minX
    y3 = math.sin(anglePi)*(f_wide-1)+math.cos(anglePi)*(f_height-1)-minY
    iSRotate2 = iSRotate
    for i in range(height-1):
        for j in range(width-1):
            if(iSRotate2[i,j].all()==0):
                avg = iSRotate[i-1,j]+iSRotate[i+1,j]+iSRotate[i,j-1]+iSRotate[i,j+1]
                iSRotate2[i,j] = avg'''

    # i>=-math.tan(anglePi)*j-minY and i<=1/math.tan(anglePi)*j-minY
    #  and i>=1/math.tan(anglePi)*(j-x3)+y3 and i<=-math.tan(anglePi)*(j-x3)+y3
    return iSRotate

运行结果

在这里插入图片描述
可以看到有很多转飞的空穴,因为是从原图找新图的对应点坐标,所以有一部分新图像素是找不到对应像素的

参考链接

链接: 参考链接.

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值