python - 如何在不使用 python 中的任何外部库的情况下裁剪 bmp 文件并将其保存?
问题描述
我想创建一个带有参数的函数(input_filename,output_filename,origin_x,origin_y,new_height,new_width)。到目前为止,我正在将文件作为字节读取,并将其附加到单独的列表中以用于单独的行。但有些不对劲,因为输出图像变得倾斜。而且我正在使用 pil 库来保存它,我不能使用它。
import numpy as np
from PIL import Image #just to save the file. but dont want to use it
import struct
def read_rows(path,origin_x,origin_y,height,width):
image_file = open(path, "rb")
image_file.seek(18,0)
bmp_w = struct.unpack('I',image_file.read(4))[0]
# print(bmp_w)
bmp_h = struct.unpack('I',image_file.read(4))[0]
# print(bmp_h)
image_file.seek(54)
if origin_x+width > bmp_w or origin_y+height > bmp_h:
print(f'Dimensions are outside the figure size, choose between {bmp_w}x{bmp_h} pixels')
return
# We need to read pixels in as rows to later swap the order
# since BMP stores pixels starting at the bottom left.
rows = []
row = []
pixel_index = 0
while True:
if pixel_index == bmp_w:
pixel_index = 0
rows.insert(0, row)
if len(row) != bmp_w * 3:
raise Exception(f"Row length is not {bmp_w*3} but " + str(len(row)) + " / 3.0 = " + str(len(row) / 3.0))
row = []
pixel_index += 1
r_string = image_file.read(1)
g_string = image_file.read(1)
b_string = image_file.read(1)
if len(r_string) == 0:
# This is expected to happen when we've read everything.
if len(rows) != bmp_h:
print (f"Warning!!! Read to the end of the file at the correct sub-pixel (red) but we've not read {bmp_h} rows!")
break
if len(g_string) == 0:
print( "Warning!!! Got 0 length string for green. Breaking.")
break
if len(b_string) == 0:
print ("Warning!!! Got 0 length string for blue. Breaking.")
break
r = ord(r_string)
g = ord(g_string)
b = ord(b_string)
row.append(b)
row.append(g)
row.append(r)
image_file.close()
return rows
def repack_sub_pixels(rows,origin_x,origin_y,height,width):
print ("Repacking pixels...")
sub_pixels = []
for jdx, row in enumerate(rows):
if jdx >=origin_y and jdx <origin_y+height:
for idx , sub_pixel in enumerate(row):
if idx >= (origin_y*3) and idx< ((origin_y+width)*3):
sub_pixels.append(sub_pixel)
print ("Packed", len(sub_pixels), "sub-pixels.")
return sub_pixels
def crop(file_name,output_name,a,b,h,w):
'''
Takes input file name ,
output file name,
origin x from left ,
origin y from top ,
height of cropped image from origin ,
width of cropped image from origin.
'''
rows = read_rows(file_name,a,b,h,w)
sub_pixels = repack_sub_pixels(rows,a,b,h,w)
print(type(sub_pixels[0]))
dim = int((len(sub_pixels)//3)**(1/2))
print(dim)
l = np.array(sub_pixels).astype(np.uint8)
l = l.reshape((h,w,3))
im = Image.fromarray(l)
im.save(f'{output_name}')
print(f'File saved as {output_name}')
crop('tiger.bmp','new_output.bmp', 0,100,100,100)
解决方案
def read_rows(path,origin_x,origin_y,height,width):
import struct
global bmp_w
global bmp_h
image_file = open(path, "rb")
image_file.seek(18,0)
bmp_w = struct.unpack('I',image_file.read(4))[0]
print(f'Width of image : {bmp_w} px')
diff = (bmp_w*3)%4
if diff != 0:
bmp_w +=diff
bmp_h = struct.unpack('I',image_file.read(4))[0]
print(f'Height of image : {bmp_h} px')
print(f'Total number of subpixels : {bmp_w*bmp_h*3} pixels')
image_file.seek(54,0)
if origin_x+width > bmp_w or origin_y+height > bmp_h:
raise ValueError(f'Dimensions are outside the figure size, choose between {bmp_w}x{bmp_h} pixels')
return
# We need to read pixels in as rows to later swap the order
# since BMP stores pixels starting at the bottom left.
test_rows = []
test_row = []
pixel_index = 0
while True:
if pixel_index == bmp_w:
pixel_index = 0
test_rows.append(test_row)
if len(test_row) != bmp_w * 3:
raise Exception(f'Row length is not {bmp_w*3} but {str(len(row))}/3.0 = {str(len(row))}/ 3.0)')
test_row = []
pixel_index += 1
r_string = image_file.read(1)
g_string = image_file.read(1)
b_string = image_file.read(1)
if len(r_string) == 0:
# This is expected to happen when we've read everything.
if len(test_rows) != bmp_h:
print (f"Warning!!! Read to the end of the file at the correct sub-pixel (red) but we've not read {bmp_h} rows!")
break
if len(g_string) == 0:
print( "Warning!!! Got 0 length string for green. Breaking.")
break
if len(b_string) == 0:
print ("Warning!!! Got 0 length string for blue. Breaking.")
break
test_row.append(r_string)
test_row.append(g_string)
test_row.append(b_string)
image_file.close()
print(f'Number of rows : {len(test_rows)}')
return test_rows
def repack_sub_pixels(test_rows,origin_x,origin_y,height,width):
print ("Repacking pixels...")
test_sub_pixels = []
diff = (width*3)%4
padding = 0
for jdx, test_row in enumerate(test_rows):
if jdx >= origin_y and jdx <origin_y+height:
for idx , sub_pixel in enumerate(test_row):
if idx >= (origin_x*3) and idx< ((origin_x+width)*3):
test_sub_pixels.append(sub_pixel)
if (diff!= 0):
pad = 0
pad = pad.to_bytes(1, 'little')
for x in range(diff):
test_sub_pixels.append(pad)
test_sub_pixels.append(pad)
test_sub_pixels.append(pad)
padding = diff
print (f'Packed {len(test_sub_pixels)} sub-pixels including padding: i.e {width+diff}x{height}x3 pixels.')
return test_sub_pixels,padding
def get_header(file_name,w,h,p,tsp,on):
li = []
print(f'Total Padding done: {p*3} bytes')
bmp = open(file_name, 'rb')
Type = bmp.read(2)
Size = bmp.read(4) #change
Reserved_1 = bmp.read(2)
Reserved_2 = bmp.read(2)
Offset = bmp.read(4)
DIB_Header_Size = bmp.read(4)
Width = bmp.read(4)
Height = bmp.read(4)
Colour_Planes = bmp.read(2)
Bits_per_Pixel = bmp.read(2)
Compression_Method = bmp.read(4)
Raw_Image_Size = bmp.read(4)
Horizontal_Resolution = bmp.read(4)
Vertical_Resolution = bmp.read(4)
Number_of_Colours = bmp.read(4)
Important_Colours = bmp.read(4)
bmp.close()
Width = w.to_bytes(4, 'little')
Height = h.to_bytes(4, 'little')
Raw_Image_Size = (((w+p)*3)*h).to_bytes(4,'little')
Size = ((((w+p)*3)*h)+54).to_bytes(4, 'little')
li.append(Type)
li.append(Size)
li.append(Reserved_1)
li.append(Reserved_2)
li.append(Offset)
li.append(DIB_Header_Size)
li.append(Width)
li.append(Height)
li.append(Colour_Planes)
li.append(Bits_per_Pixel)
li.append(Compression_Method)
li.append(Raw_Image_Size)
li.append(Horizontal_Resolution)
li.append(Vertical_Resolution)
li.append(Number_of_Colours)
li.append(Important_Colours)
for x in tsp:
li.append(x)
with open(f'./{on}', 'wb') as file:
for x in li:
file.write(x)
file.close
def crop(file_name,output_name,a,b,h,w):
'''
Takes input file name ,
output file name,
origin x from left ,
origin y from top ,
height of cropped image from origin ,
width of cropped image from origin.
'''
test_rows = read_rows(file_name,a,b,h,w)
test_sub_pixels,padding = repack_sub_pixels(test_rows,a,b,h,w)
dim = int((len(test_sub_pixels))//h)
w = dim//3
print(f'length of columns : {dim} ')
print(f'New width of image with padding: {w}')
print(f'New height of image: {h}')
get_header(file_name,w,h,padding,test_sub_pixels,output_name)
print('Image Saved')
'''
CALL FUNCTION HERE
'''
while(1):
p1,p2,p3,p4,p5,p6 = input('To Crop the Image, Enter in following format input_filename,output_filename,origin_x,origin_y,height,width\n').split(sep=',')
p3 = int(p3)
p4 = int(p4)
p5 = int(p5)
p6 = int(p6)
crop(p1,p2,p3,p4,p5,p6 )
推荐阅读
- visual-studio - 我想在 Visual Studio Code 中添加一个新终端
- java - Javadoc 链接到当前方法实现,这可能吗?
- c - 循环求和字符串 C 中的数字
- html - MySql Workbench 和 Html Sublimetext 3
- php - Visual Studio Code PHP 验证错误:无法验证,因为 /usr/bin/php 不是有效的 php 可执行文件
- javascript - HTML CSS/JS 底部导航栏向上滑动
- sql - 计算同一用户 sql 不同操作之间的小时/天差异
- asp.net - ValidateRequest=False 可以在 asp.net webform 中有条件吗?
- python - Pandas groupby 给出任何非 nan 值
- javascript - 超过最大更新深度。当组件重复调用 setState 时,可能会发生这种情况