来自二进制值的枕头黑白图像
Pillow black and white image from binary values
我有一个包含图像像素数据的二进制文件,该文件仅包含等于 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 为黑色。
Pillow 中是否有功能“只知道” 我想要 1=white & 0=black(而不是 255=white & 0=黑色)?
编辑
在下面的答案的帮助下,我找到了很多解决方案,我可以在这些解决方案中修改我的数据以将 1 替换为 255...我已经发布了包含这些解决方案结果的答案。其中一些速度非常快,因此可能不会获得更多性能。
但如果有一个很好的解决方案可以完全避免这种开销,i.e.to 直接告诉 Pillow 将 1 视为白色,将 0 视为黑色,那将是理想的。
你可以边读边改数据,像这样:
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', (h,w), 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', (h,w), 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', (h,w), data_new)
img.save('img_new.png')
输出(img.png / img_new.png):
更新
感谢@MarkSetchell 的评论将我指向 ,我使用 Palette 直接告诉 Pillow 将 0 视为黑色,将 1 视为白色。
代码如下:
def create_images_palette():
palette = [ 0, 0, 0,
255,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
所以调色板解决方案获胜。
在答案的帮助下,我测试了一些解决方案,我可以在这些解决方案中修改我的数据以将 1 替换为 255。以下是这些测试的结果。
我会接受关于这个问题的答案,根据问题,告诉 Pillow 直接将 1 视为白色,将 0 视为黑色。 如果做不到,这些解决方案中的一些为我的需要工作和表现良好。
请注意,在我的实际应用程序中,我可以在一个二进制文件中包含大量图像 back-to-back 的数据。这些解决方案反映了这一点。
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, 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), b''):
img = Image.frombuffer('L', (h,w), data)
img = ImageOps.autocontrast(img)
imgs.append(img)
return imgs
def create_images2():
imgs = []
with open(filename, 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), b''):
data = bytes([0 if b==0 else 255 for b in data])
img = Image.frombuffer('L', (h,w), data)
imgs.append(img)
return imgs
def create_images3():
imgs = []
with open(filename, 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), 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', (h,w), 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', (h,w), 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, w*h), 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', 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), b''):
img = Image.frombuffer('L', (h,w), 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,)]', number=number, globals=globals()) / number)
print("create_images3", timeit.timeit('[func() for func in (create_images3,)]', number=number, globals=globals()) / number)
print("create_images4", timeit.timeit('[func() for func in (create_images4,)]', number=number, globals=globals()) / number)
print("create_images5", timeit.timeit('[func() for func in (create_images5,)]', number=number, globals=globals()) / number)
print("create_images6", timeit.timeit('[func() for func in (create_images6,)]', number=number, globals=globals()) / number)
print("create_images7", timeit.timeit('[func() for func in (create_images7,)]', number=number, globals=globals()) / number)
print("create_images8", timeit.timeit('[func() for func in (create_images8,)]', number=number, globals=globals()) / number)
print("create_images9", timeit.timeit('[func() for func in (create_images9,)]', number=number, 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
我有一个包含图像像素数据的二进制文件,该文件仅包含等于 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 为黑色。
Pillow 中是否有功能“只知道” 我想要 1=white & 0=black(而不是 255=white & 0=黑色)?
编辑
在下面的答案的帮助下,我找到了很多解决方案,我可以在这些解决方案中修改我的数据以将 1 替换为 255...我已经发布了包含这些解决方案结果的答案。其中一些速度非常快,因此可能不会获得更多性能。
但如果有一个很好的解决方案可以完全避免这种开销,i.e.to 直接告诉 Pillow 将 1 视为白色,将 0 视为黑色,那将是理想的。
你可以边读边改数据,像这样:
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', (h,w), 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', (h,w), 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', (h,w), data_new)
img.save('img_new.png')
输出(img.png / img_new.png):
更新
感谢@MarkSetchell 的评论将我指向
代码如下:
def create_images_palette():
palette = [ 0, 0, 0,
255,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
所以调色板解决方案获胜。
在答案的帮助下,我测试了一些解决方案,我可以在这些解决方案中修改我的数据以将 1 替换为 255。以下是这些测试的结果。
我会接受关于这个问题的答案,根据问题,告诉 Pillow 直接将 1 视为白色,将 0 视为黑色。 如果做不到,这些解决方案中的一些为我的需要工作和表现良好。
请注意,在我的实际应用程序中,我可以在一个二进制文件中包含大量图像 back-to-back 的数据。这些解决方案反映了这一点。
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, 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), b''):
img = Image.frombuffer('L', (h,w), data)
img = ImageOps.autocontrast(img)
imgs.append(img)
return imgs
def create_images2():
imgs = []
with open(filename, 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), b''):
data = bytes([0 if b==0 else 255 for b in data])
img = Image.frombuffer('L', (h,w), data)
imgs.append(img)
return imgs
def create_images3():
imgs = []
with open(filename, 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), 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', (h,w), 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', (h,w), 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, w*h), 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', 'rb') as ifile:
for data in iter(partial(ifile.read, w*h), b''):
img = Image.frombuffer('L', (h,w), 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,)]', number=number, globals=globals()) / number)
print("create_images3", timeit.timeit('[func() for func in (create_images3,)]', number=number, globals=globals()) / number)
print("create_images4", timeit.timeit('[func() for func in (create_images4,)]', number=number, globals=globals()) / number)
print("create_images5", timeit.timeit('[func() for func in (create_images5,)]', number=number, globals=globals()) / number)
print("create_images6", timeit.timeit('[func() for func in (create_images6,)]', number=number, globals=globals()) / number)
print("create_images7", timeit.timeit('[func() for func in (create_images7,)]', number=number, globals=globals()) / number)
print("create_images8", timeit.timeit('[func() for func in (create_images8,)]', number=number, globals=globals()) / number)
print("create_images9", timeit.timeit('[func() for func in (create_images9,)]', number=number, 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