问题描述
我有一个二进制文件,其中包含仅包含等于0或1(0x00或0x01)的字节的图像像素数据。我想根据这些数据创建黑白图像。
到目前为止,我的代码在Python中使用Pillow的可重现示例(请注意,通常我将从文件中加载数据,而不是就地创建数据):
import numpy as np
from PIL import Image,ImageOps
w = 128 # image Could be much bigger,keeping it small for the example
h = 128
data = np.random.randint(2,size=w*h,dtype=np.uint8).tobytes()
img = Image.frombuffer('L',(h,w),data)
img.show()
问题是,图像将像素数据解释为灰度,因此值1几乎是黑色。我希望1为白色(即255灰度),0为黑色。
枕头中是否有功能“我只知道” 我想要1 =白色&0 =黑色(而不是255 = white&0 =黑色)?
编辑
借助下面的答案,我找到了许多解决方案,在这些解决方案中,我可以修改数据以将255替换为1。其中一些速度非常快,因此可能不会获得更多的性能。
但是,如果有一个很好的解决方案可以完全避免这种开销,即直接告诉Pillow将1视为白色,将0视为黑色,那将是理想的选择。
解决方法
更新
由于@MarkSetchell的评论使我指向https://stackoverflow.com/a/64682849/2836621,因此我使用了Palette's告诉Pillow将0视为黑色,将1视为白色。
代码如下:
def create_images_palette():
palette = [ 0,255,255]
palette = palette + [0]*(768-len(palette))
imgs = []
with open(filename,'rb') as ifile:
for data in iter(partial(ifile.read,w*h),b''):
img = Image.frombuffer('L',(h,w),data)
img.putpalette(palette)
imgs.append(img)
return imgs
与以下测试的获胜者相比,结果,但是这次我使用w = 1024,h = 1024,N = 1000(对于我的用法更现实):
create_images3 0.42854620320013054
create_images6 0.32936501539988966
create_images7 0.31196588300008443
create_images_palette 0.21011565389999304
所以调色板解决方案会获胜。
借助答案,我测试了许多解决方案,可以在其中修改数据以将255替换为1。这是这些测试的结果。
对于这个问题,我会接受一个答案,根据这个问题,它告诉Pillow直接将1视为白色,将0视为黑色。失败的是,其中一些解决方案对于我的需求。
请注意,在我的实际应用程序中,我可以在一个二进制文件中连续存储大量图像的数据。这些解决方案反映了这一点。
import numpy as np
import os
from functools import partial
from PIL import Image,ImageOps
w = 128 # image could be much bigger,keeping it small for the example
h = 128
N = 100
filename = 'byte_imgs.dat'
data = np.random.randint(2,size=w*h*N,dtype=np.uint8).tobytes()
f = open(filename,'wb')
f.write(data)
f.close()
print("image data written to file")
def create_images1():
imgs = []
with open(filename,data)
img = ImageOps.autocontrast(img)
imgs.append(img)
return imgs
def create_images2():
imgs = []
with open(filename,b''):
data = bytes([0 if b==0 else 255 for b in data])
img = Image.frombuffer('L',data)
imgs.append(img)
return imgs
def create_images3():
imgs = []
with open(filename,b''):
mem = memoryview(data).cast('B',shape=[w,h])
arr = np.asarray(mem)
img = Image.fromarray(arr*255)
imgs.append(img)
return imgs
def create_images4():
data = bytearray(w*h)
imgs = []
with open(filename,"rb") as f:
byte = f.read(1)
while byte != b'':
for i in range(w*h):
data[i] = int.from_bytes(byte,"big") * 0xFF
byte = f.read(1)
img = Image.frombuffer('L',bytes(data))
imgs.append(img)
return imgs
def create_images5():
imgs = []
with open(filename,"rb") as f:
i = 0
data = bytearray()
byte = f.read(1)
while byte != b'':
if byte != b'\x00':
data.append(0xff)
else:
data.append(0x00)
byte = f.read(1)
i+=1
if i == w*h:
img = Image.frombuffer('L',bytes(data))
imgs.append(img)
i=0
data = bytearray()
return imgs
def create_images6():
imgs = []
with open(filename,'rb') as ifile:
while True:
arr = np.fromfile(ifile,dtype=np.uint8,count=w*h)
if arr.size < w*h:
break
img = Image.fromarray(arr.reshape(w,h)*255)
imgs.append(img)
return imgs
def create_images7():
imgs = []
with open(filename,'rb') as ifile:
for dat in iter(partial(ifile.read,b''):
arr = np.frombuffer(dat,dtype=np.uint8).reshape((w,h))
img = Image.fromarray(arr*255)
imgs.append(img)
return imgs
def create_images8():
imgs = []
data = np.fromfile(filename,dtype=np.int8)
n = int(data.size / (w*h))
for i in range(n):
offset = i*w*h
state = np.reshape(data[offset:offset+w*h],(w,h))
img = Image.fromarray(state*255)
imgs.append(img)
return imgs
def create_images9():
os.system(r"bbe -e 's/\x01/\xff/g' byte_imgs.dat > byte_imgs_new.dat")
imgs = []
with open('byte_imgs_new.dat',data)
imgs.append(img)
return imgs
import timeit
number = 10
print("create_images1",timeit.timeit('[func() for func in (create_images1,)]',number=number,globals=globals()) / number)
print("create_images2",timeit.timeit('[func() for func in (create_images2,globals=globals()) / number)
print("create_images3",timeit.timeit('[func() for func in (create_images3,globals=globals()) / number)
print("create_images4",timeit.timeit('[func() for func in (create_images4,globals=globals()) / number)
print("create_images5",timeit.timeit('[func() for func in (create_images5,globals=globals()) / number)
print("create_images6",timeit.timeit('[func() for func in (create_images6,globals=globals()) / number)
print("create_images7",timeit.timeit('[func() for func in (create_images7,globals=globals()) / number)
print("create_images8",timeit.timeit('[func() for func in (create_images8,globals=globals()) / number)
print("create_images9",timeit.timeit('[func() for func in (create_images9,globals=globals()) / number)
结果
以秒为单位报告的每个功能的平均运行时间。 create_images3()
和create_images7()
是这次测试的明显赢家。
create_images1 0.012226119600018136
create_images2 0.09197459420001905
create_images3 0.0021811368000271615
create_images4 0.30249598119999066
create_images5 0.3393335546000344
create_images6 0.0033311289999801374
create_images7 0.0021913534999839614
create_images8 0.015457254699958867
create_images9 0.044248268000046664
,
您可以在读取数据时更改数据,如下所示:
with open("file.dat","rb") as f:
byte = f.read(1)
while byte != b"":
if byte !=0:
# append to data 255
else:
# append to data 0
byte = f.read(1)
,
尝试这样做,在发送到PIL之前更改缓冲区:
import numpy as np
from PIL import Image,ImageOps
w = 128
h = 128
data = np.random.randint(2,size=w*h,dtype=np.uint8).tobytes()
data = bytes([0 if b==0 else 255 for b in data])
img = Image.frombuffer('L',data)
img.show()
,
您可以尝试使用bbe
https://sourceforge.net/projects/bbe-/或类似方法在源数据中将“ 1”更改为“ 255”。
bbe -e 's/\x01/\xff/g' file.dat > file_new.dat
它改变了:
\x01\x00\x00\x01\x01\x01\x00\x00\x00\x00...
收件人:
\xff\x00\x00\xff\xff\xff\x00\x00\x00\x00...
这是我的测试代码(请确定):
import random
from PIL import Image,ImageOps
import os
w = 128
h = 128
# make the data array
data = b''.join([random.randint(0,1).to_bytes(1,'big') for _ in range(w*h)])
# save the data to a file
file = open('file.dat','wb')
file.write(data)
file.close()
# make an image with autocontrast
img = Image.frombuffer('L',data)
img = ImageOps.autocontrast(img)
img.save('img.png')
# replace bytes in the data file
os.system(r"bbe -e 's/\x01/\xff/g' file.dat > file_new.dat")
# read the new data
file = open('file_new.dat','rb')
data_new = file.read()
file.close()
# make an image with no autocontrast
img = Image.frombuffer('L',data_new)
img.save('img_new.png')
输出(img.png / img_new.png):