第一次写博客,记录自己的作业!不要直接照搬照抄哦!
前面的读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
可以看到有很多转飞的空穴,因为是从原图找新图的对应点坐标,所以有一部分新图像素是找不到对应像素的
链接: 参考链接.