用二进制值枕住黑白图像 更新结果

问题描述

我有一个二进制文件,其中包含仅包含等于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):

enter image description here