定长REL压缩
压缩代码:
from PIL import Image
import numpy as np
import os
#打开一张二元图片并压缩成以REL_为前缀的文件
img_name=input("请输入图片名:")
size1=os.stat('./'+img_name)
size1=size1.st_size # 原图片的大小
img1=Image.open(img_name)
img1=img1.convert('1')
img1_a=np.array(img1)
sz=img1_a.shape
cnt=int(0)
flag=img1_a[0][0]
L=len(img_name)
for i in range(L):
if(img_name[i]=='.'):
img_name=img_name[0:i]
break
f=open("./REL_"+img_name,"wb")
# 存入图片的大小数据
h=sz[0]
w=sz[1]
lst=""
lst1=""
# 先高度后宽度
while(h):
n=h%2
lst=str(n)+lst
h=int(h/2)
while(len(lst)<16): #补齐成两个字节
lst='0'+lst
hexStr=hex(int(lst,2))
hexStr=hexStr[2:]
if(len(hexStr)%2!=0):
hexStr='0'+hexStr
f.write(bytes.fromhex(hexStr)) # bytes.fromhex(hex)的hex字串的位数必须是2的倍数
lst=""
while(w):
n=w%2
lst=str(n)+lst
w=int(w/2)
while(len(lst)<16): #补齐成两个字节
lst='0'+lst
hexStr=hex(int(lst,2))
hexStr=hexStr[2:]
if(len(hexStr)%2!=0):
hexStr='0'+hexStr
f.write(bytes.fromhex(hexStr))
lst=""
for i in range(sz[0]):
# print(i)
for j in range(sz[1]):
exp=img1_a[i][j]
if(img1_a[i][j]!=flag or cnt==7):
while(cnt):
k=cnt%2
if(k):
lst='1'+lst
else:
lst='0'+lst
cnt=int(cnt/2)
while(len(lst)<3):
lst='0'+lst
if(flag):
lst='1'+lst
else:
lst='0'+lst
lst1=lst1+lst
lst=""
if(len(lst1)==8):
hexStr=hex(int(lst1,2))
f.write(bytes.fromhex(hexStr[2:]))
lst1=""
flag=img1_a[i][j]
cnt=1
else:
cnt+=1
if(lst):
while(cnt):
k=cnt%2
if(k):
lst='1'+lst
else:
lst='0'+lst
cnt/=2
while(len(lst)<3):
lst='0'+lst
if(flag):
lst='1'+lst
else:
lst='0'+lst
for i in range(4):
lst=lst+'0'
hexStr=hex(int(lst,2))
f.write(bytes.fromhex(hexStr[2:]))
print("已压缩为文件:"+"./REL_"+img_name)
f.close()
size2=os.stat("./REL_"+img_name)
size2=size2.st_size
rate=(size2/size1)*100
print("压缩率为{:.2f}%".format(rate))
解压代码:
from PIL import Image
import numpy as np
name=input("请输入REL压缩后的文件:")
M_=np.zeros([2,16]) # 存放图片大小数据
f=open(name,'rb')
# 先读取图片的大小
h=f.read(2) # 和汇编读取字节不一样,纯按顺序
hexStr=h.hex()
height=int(hexStr,16) # 高度
h=f.read(2)
hexStr=h.hex()
width=int(hexStr,16) # 宽度
M=np.zeros([height,width]) # 传入一个列表来表示0矩阵的规模,行x列
pos=0 #计数器
while(1):
h=f.read(1)
if(len(h)==0):
break
HX=h.hex()
# print(HX)
try:
HX=int(HX,16)
except:
print("Q1;"+h)
binStr=""
while(HX): # 转化成二进制数据
n=HX%2
binStr=str(n)+binStr
HX=int(HX/2)
while(len(binStr)<8):
binStr='0'+binStr
for i in range(2):
try:
numM=int(binStr[4*i])
times=int(binStr[4*i+1:4*i+4],2)
except:
print(pos)
print(binStr)
for j in range(times):
if(int(numM)):
M[int(pos/width)][pos%width]=300
else:
M[int(pos/width)][pos%width]=0
# print(pos)
pos+=1
f.close()
img_new=Image.fromarray(M)
img_new=img_new.convert('1') # 默认为浮点类型F,无法直接存储
img_new.save("./"+name+"_new.jpg")
print("已输出为图片:"+"./"+name+"_new.jpg")
合理地存储压缩信息能够有效提高压缩率,最糟糕的办法就是将压缩信息以文本方式存入文件中,这样做会起到负压缩的效果。将压缩信息保存为文本是非常浪费的,于是考虑将其保存在字节信息中。
采取定长REL压缩,取4位二进制数来保存每一段元素,一段元素最长为7个,存储为二进制的“000”,还有一位用于保存压缩连续的元素是1还是0,如要保存连续出现6次的元素1,6的二进制表示为“110”,因此最后存储为“1110”,第一位的1表示连续的元素是1,后面的“110”表示该元素出现了6次,两段元素拼接成一个字节,利用python的字节流将其存入文件中即可。读取时只要将该过程逆运算即可得到图片信息。为了保存图片的大小,在压缩信息前额外增添四个字节用于存储图片的尺寸,分别用两个字节(16位二进制最多可以表示十进制的65535)保存图片的高度和宽度。
参考:[
[文件流](https://www.likecs.com/show-204965563.html#sc=2100 python),
]
标签:HX,name,img,压缩,定长,REL,img1 From: https://www.cnblogs.com/Forest-set-you/p/16934298.html