bmp-resize-python/main.py

353 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from math import floor, ceil
from tqdm import tqdm
################## 数据输入 Start ##################
# 输入数据
img = input("请输入图片路径:")
new_width = int(input("请输入新宽度(px)"))
new_height = int(input("请输入新高度(px)"))
print("模式 0 双线性插值")
print("模式 1 最邻近插值")
print("模式 2 高斯模糊缩放")
mode = int(input("请输入模式:"))
# img = 'imgs/text.bmp'
# new_width = 2560 * 3
# new_height = 2560
# mode = 0
if (mode not in [0, 1, 2]):
mode = 0
################## 数据输入 End ##################
################## 文件读入 Start ##################
# 读入图片
with open(img, 'rb') as f:
imgBytes = f.read()
for _ in tqdm(range(len(imgBytes)), "读入文件中"):
pass
################## 文件读入 End ##################
# 十六进制转十进制
def byteSize(begin, length):
size = 0
for i in range(length):
size += imgBytes[i + begin] * 16**(2 * i)
return size
# 十进制转十六进制
def sizeByte(size, length):
b = []
while (size):
b.append(size // 16**((length - len(b) - 1) * 2))
size %= 16**((length - len(b)) * 2)
b.extend([0] * (length - len(b)))
return b[::-1]
################## 文件头处理 Start ##################
# 位图文件头
# 检验文件头是否为 BM
if (imgBytes[0] != 66 or imgBytes[1] != 77):
print("当前文件非 BMP 格式")
exit()
fileSize = byteSize(2, 4) #文件头中的文件大小
dataStart = byteSize(10, 4) #文件头中的数据开始字节
# bmp 文件头
headerSize = byteSize(14, 4) #该头结构的大小40字节
width = byteSize(18, 4) #位图宽度,单位为像素(有符号整数)
height = byteSize(22, 4) #位图高度,单位为像素(有符号整数)
nbplan = byteSize(26, 2) #色彩平面数只有1为有效值
bpp = byteSize(28, 2) #每个像素所占位数即图像的色深。典型值为1、4、8、16、24和32
compression = byteSize(30, 4) #所使用的压缩方法,可取值见下表。
imageSize = byteSize(34, 4) #图像大小。指原始位图数据的大小(详见后文),与文件大小不是同一个概念。
wppm = byteSize(38, 4) #图像的横向分辨率,单位为像素每米(有符号整数)
hppm = byteSize(42, 4) #图像的纵向分辨率,单位为像素每米(有符号整数)
colorsNum = byteSize(46, 4) #调色板的颜色数为0时表示颜色数为默认的2^色深个
icolorsNum = byteSize(50, 4) #重要颜色数为0时表示所有颜色都是重要的通常不使用本项
colorsBoard = imgBytes[54:dataStart] #调色板
colors = []
i = 0
while (i < len(colorsBoard)):
colors.append({
'r': colorsBoard[i],
'g': colorsBoard[i + 1],
'b': colorsBoard[i + 2],
'a': colorsBoard[i + 3],
})
i += 4
if (not (bpp in [24, 32])):
print("不支持该图片")
exit()
################## 文件头处理 End ##################
################## 像素点读入 Start ##################
# 变量预处理
pixels = []
for i in range(height):
pixels.append([])
rowLength = floor(width * bpp / 8)
while (rowLength % 4 != 0 or rowLength == 0):
rowLength += 1
# colorsIndex = []
# if (bpp == 1):
# initPixel = imgBytes[dataStart] // 128
# colorsIndex.append()
# 计算像素点
i = dataStart
for currentRow in tqdm(range(height), "读入像素点中"):
currentCol = 0
# if (bpp in [1, 2, 4, 8]):
# # 其实可以直接存入索引,之后再取出,但是为了易于理解,这里直接将像素数据插入 pixels 数组,这会导致效率的损失
# b = 0
# # while(b < width*bpp):
# # if(bpp==1):
# # print("")
# else:
while (currentCol < width * (bpp // 8)):
if (bpp == 32):
pixels[currentRow].append({
'r':
imgBytes[dataStart + currentRow * rowLength + currentCol],
'g':
imgBytes[dataStart + currentRow * rowLength + currentCol + 1],
'b':
imgBytes[dataStart + currentRow * rowLength + currentCol + 2],
'a':
imgBytes[dataStart + currentRow * rowLength + currentCol + 3]
})
currentCol += bpp // 8
else:
pixels[currentRow].append({
'r':
imgBytes[dataStart + currentRow * rowLength + currentCol],
'g':
imgBytes[dataStart + currentRow * rowLength + currentCol + 1],
'b':
imgBytes[dataStart + currentRow * rowLength + currentCol + 2],
'a':
0
})
currentCol += bpp // 8
# 读入图片为像素数组完成
# 缩放图片
# 变量预处理
scale_w = new_width / width
scale_h = new_height / height
newpixels = []
for i in range(new_height):
newpixels.append([])
# 辅助计算函数
def linear_single(x, x1, x2, f1, f2):
return floor(f1 * (x2 - x) / (x2 - x1) + f2 * (x - x1) / (x2 - x1))
# 双线性插值函数
# 参考 https://zh.wikipedia.org/wiki/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC
def linear_insert(row, col):
x = row / scale_h
y = col / scale_w
# 如果整数格点就直接返回
x1 = floor(x)
x2 = ceil(x)
y1 = floor(y)
y2 = ceil(y)
if (x2 >= height):
x2 = x1
if (y2 >= width):
y2 = y1
if (x1 == x2 and y1 == y2):
return pixels[x1][y1]
if (x1 == x2):
x = x1
r = linear_single(y, y1, y2, pixels[x][y1]['r'], pixels[x][y2]['r'])
g = linear_single(y, y1, y2, pixels[x][y1]['g'], pixels[x][y2]['g'])
b = linear_single(y, y1, y2, pixels[x][y1]['b'], pixels[x][y2]['b'])
a = linear_single(y, y1, y2, pixels[x][y1]['a'], pixels[x][y2]['a'])
return {'r': r, 'g': g, 'b': b, 'a': a}
if (y1 == y2):
y = y1
r = linear_single(x, x1, x2, pixels[x1][y]['r'], pixels[x2][y]['r'])
g = linear_single(x, x1, x2, pixels[x1][y]['g'], pixels[x2][y]['g'])
b = linear_single(x, x1, x2, pixels[x1][y]['b'], pixels[x2][y]['b'])
a = linear_single(x, x1, x2, pixels[x1][y]['a'], pixels[x2][y]['a'])
return {'r': r, 'g': g, 'b': b, 'a': a}
fy1_r = linear_single(x, x1, x2, pixels[x1][y1]['r'], pixels[x2][y1]['r'])
fy1_g = linear_single(x, x1, x2, pixels[x1][y1]['g'], pixels[x2][y1]['g'])
fy1_b = linear_single(x, x1, x2, pixels[x1][y1]['b'], pixels[x2][y1]['b'])
fy1_a = linear_single(x, x1, x2, pixels[x1][y1]['a'], pixels[x2][y1]['a'])
fy2_r = linear_single(x, x1, x2, pixels[x1][y2]['r'], pixels[x2][y2]['r'])
fy2_g = linear_single(x, x1, x2, pixels[x1][y2]['g'], pixels[x2][y2]['g'])
fy2_b = linear_single(x, x1, x2, pixels[x1][y2]['b'], pixels[x2][y2]['b'])
fy2_a = linear_single(x, x1, x2, pixels[x1][y2]['a'], pixels[x2][y2]['a'])
r = linear_single(y, y1, y2, fy1_r, fy2_r)
g = linear_single(y, y1, y2, fy1_g, fy2_g)
b = linear_single(y, y1, y2, fy1_b, fy2_b)
a = linear_single(y, y1, y2, fy1_a, fy2_a)
return {'r': r, 'g': g, 'b': b, 'a': a}
# 计算新像素
for currentRow in tqdm(range(new_height), "计算新像素点中"):
for currentCol in range(new_width):
if (mode == 0):
newpixels[currentRow].append(linear_insert(currentRow, currentCol))
elif (mode == 1):
ori_row = floor(currentRow / scale_h)
ori_col = floor(currentCol / scale_w)
newpixels[currentRow].append(pixels[ori_row][ori_col])
# 处理新文件
newimgArray = [66, 77]
rowLength = new_width
rowLength *= bpp // 8
while (rowLength % 4 != 0):
rowLength += 1
# 新文件头
new_fileSize = 54 + rowLength * new_height #文件头中的文件大小
newimgArray.extend(sizeByte(new_fileSize, 4))
newimgArray.extend(sizeByte(0, 4))
new_dataStart = dataStart
newimgArray.extend(sizeByte(new_dataStart, 4))
# 新 bmp 文件头
new_headerSize = headerSize
newimgArray.extend(sizeByte(headerSize, 4))
new_width_b = sizeByte(new_width, 4)
newimgArray.extend(new_width_b)
new_height_b = sizeByte(new_height, 4)
newimgArray.extend(new_height_b)
new_nbplan = nbplan
newimgArray.extend(sizeByte(new_nbplan, 2))
new_bpp = bpp
newimgArray.extend(sizeByte(bpp, 2))
new_compression = compression
newimgArray.extend(sizeByte(new_compression, 4))
new_imageSize = rowLength * new_height
newimgArray.extend(sizeByte(new_imageSize, 4))
new_wppm = wppm
newimgArray.extend(sizeByte(new_wppm, 4))
new_hppm = hppm
newimgArray.extend(sizeByte(new_hppm, 4))
new_colorsNum = colorsNum
newimgArray.extend(sizeByte(new_colorsNum, 4))
new_icolorsNum = icolorsNum
newimgArray.extend(sizeByte(new_icolorsNum, 4))
new_colorsBoard = colorsBoard
for color in new_colorsBoard:
newimgArray.append(color)
def flur(row, col):
if (row - 1 < 0):
row = new_height + 1
if (row + 1 >= new_height):
row = 0
if (col - 1 < 0):
col = new_width + 1
if (col + 1 >= new_width):
col = 0
p = [0.0947416, 0.118318, 0.147761]
r = floor(newpixels[row - 1][col - 1]['r'] * p[0] +
newpixels[row - 1][col + 1]['r'] * p[0] +
newpixels[row + 1][col - 1]['r'] * p[0] +
newpixels[row + 1][col + 1]['r'] * p[0] +
newpixels[row][col + 1]['r'] * p[1] +
newpixels[row][col - 1]['r'] * p[1] +
newpixels[row - 1][col]['r'] * p[1] +
newpixels[row + 1][col]['r'] * p[1] +
newpixels[row][col]['r'] * p[2])
g = floor(newpixels[row - 1][col - 1]['g'] * p[0] +
newpixels[row - 1][col + 1]['g'] * p[0] +
newpixels[row + 1][col - 1]['g'] * p[0] +
newpixels[row + 1][col + 1]['g'] * p[0] +
newpixels[row][col + 1]['g'] * p[1] +
newpixels[row][col - 1]['g'] * p[1] +
newpixels[row - 1][col]['g'] * p[1] +
newpixels[row + 1][col]['g'] * p[1] +
newpixels[row][col]['g'] * p[2])
b = floor(newpixels[row - 1][col - 1]['b'] * p[0] +
newpixels[row - 1][col + 1]['b'] * p[0] +
newpixels[row + 1][col - 1]['b'] * p[0] +
newpixels[row + 1][col + 1]['b'] * p[0] +
newpixels[row][col + 1]['b'] * p[1] +
newpixels[row][col - 1]['b'] * p[1] +
newpixels[row - 1][col]['b'] * p[1] +
newpixels[row + 1][col]['b'] * p[1] +
newpixels[row][col]['b'] * p[2])
if (new_bpp // 8 == 3):
return [r, g, b]
else:
a = floor(newpixels[row - 1][col - 1]['a'] * p[0] +
newpixels[row - 1][col + 1]['a'] * p[0] +
newpixels[row + 1][col - 1]['a'] * p[0] +
newpixels[row + 1][col + 1]['a'] * p[0] +
newpixels[row][col + 1]['a'] * p[1] +
newpixels[row][col - 1]['a'] * p[1] +
newpixels[row - 1][col]['a'] * p[1] +
newpixels[row + 1][col]['a'] * p[1] +
newpixels[row][col]['a'] * p[2])
return [r, g, b, a]
for i in tqdm(range(len(newpixels)), "将像素点格式化中"):
row = newpixels[i]
for col in range(len(row)):
if (mode == 2):
newimgArray.extend(flur(i, col))
else:
if (new_bpp // 8 == 3):
pixel = [
newpixels[i][col]['r'], newpixels[i][col]['g'],
newpixels[i][col]['b']
]
else:
pixel = [
newpixels[i][col]['r'], newpixels[i][col]['g'],
newpixels[i][col]['b'], newpixels[i][col]['a']
]
newimgArray.extend(pixel)
newimgArray.extend(sizeByte(0, rowLength - len(row) * (new_bpp // 8)))
# 写入新的文件
newimgBytes = bytes(newimgArray)
with open(img[:-4] + "_" + str(mode) + "_new" + img[-4:], 'wb') as f:
f.write(newimgBytes)
for _ in tqdm(range(len(newimgBytes)), "写出图片中"):
pass