将图像转换为索引的 2 位灰度 BMP
converting images to indexed 2-bit grayscale BMP
首先,我的问题与 How do I convert image to 2-bit per pixel? 不同,不幸的是,它的解决方案不适用于我的情况...
我需要将图像转换为每像素 2 位灰度 BMP 格式。示例图像具有以下属性:
Color Model: RGB
Depth: 4
Is Indexed: 1
Dimension: 800x600
Size: 240,070 bytes (4 bits per pixel but only last 2 bits are used to identify the gray scales as 0/1/2/3 in decimal or 0000/0001/0010/0011 in binary, plus 70 bytes BMP metadata or whatever)
示例BMP图像开头部分的十六进制值:
3代表图像开头的白色像素。再往下是一些代表黑色、深灰、浅灰的0、1、2:
使用下面的命令,
convert pic.png -colorspace gray +matte -depth 2 out.bmp
我可以在视觉上得到正确的 4 级灰度图像,但每个像素的深度或大小错误:
Color Model: RGB
Depth: 8 (expect 4)
Dimension: 800x504
Size: 1,209,738 bytes (something like 3 bytes per pixel, plus metadata)
(no mention of indexed colour space)
请帮忙...
看看 https://en.wikipedia.org/wiki/BMP_file_format#File_structure 。
问题是您没有指定颜色 table。根据维基百科文章,如果位深度小于 8 位,则这些是强制性的。
好的,我已经按照 Mark 的提示(请参阅原始问题下的评论)编写了一个 Python 脚本来手动创建具有 4bpp 的 4 级灰度 BMP。这种特定的BMP格式结构是针对微影的4.3英寸电子纸显示模块。规格可以在这里找到:http://www.waveshare.com/wiki/4.3inch_e-Paper
以下是将原始图像通过管道传输到我的代码并保存结果的方法。
convert in.png -colorspace gray +matte -colors 4 -depth 2 -resize '800x600>' pgm:- | ./4_level_gray_4bpp_BMP_converter.py > out.bmp
4_level_gray_4bpp_BMP_converter.py
的内容:
#!/usr/bin/env python
"""
### Sample BMP header structure, total = 70 bytes
### !!! little-endian !!!
Bitmap file header 14 bytes
42 4D "BM"
C6 A9 03 00 FileSize = 240,070 <= dynamic value
00 00 Reserved
00 00 Reserved
46 00 00 00 Offset = 70 = 14+56
DIB header (bitmap information header)
BITMAPV3INFOHEADER 56 bytes
28 00 00 00 Size = 40
20 03 00 00 Width = 800 <= dynamic value
58 02 00 00 Height = 600 <= dynamic value
01 00 Planes = 1
04 00 BitCount = 4
00 00 00 00 compression
00 00 00 00 SizeImage
00 00 00 00 XPerlPerMeter
00 00 00 00 YPerlPerMeter
04 00 00 00 Colours used = 4
00 00 00 00 ColorImportant
00 00 00 00 Colour definition index 0
55 55 55 00 Colour definition index 1
AA AA AA 00 Colour definition index 2
FF FF FF 00 Colour definition index 3
"""
# to insert File Size, Width and Height with hex strings in order
BMP_HEADER = "42 4D %s 00 00 00 00 46 00 00 00 28 00 00 00 %s %s 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 55 55 55 00 AA AA AA 00 FF FF FF 00"
BMP_HEADER_SIZE = 70
BPP = 4
BYTE = 8
ALIGNMENT = 4 # bytes per row
import sys
from re import findall
DIMENTIONS = 1
PIXELS = 3
BLACK = "0"
DARK_GRAY = "1"
GRAY = "2"
WHITE = "3"
# sample data:
# ['P5\n', '610 590\n', '255\n', '<1 byte per pixel for 4 levels of gray>']
# where item 1 is always P5, item 2 is width heigh, item 3 is always 255, items 4 is pixels/colours
data = sys.stdin.readlines()
width = int(data[DIMENTIONS].strip().split(' ')[0])
height = int(data[DIMENTIONS].strip().split(' ')[1])
if not width*height == len(data[PIXELS]):
print "Error: pixel data (%s bytes) and image size (%dx%d pixels) do not match" % (len(data[PIXELS]),width,height)
sys.exit()
colours = [] # enumerate 4 gray levels
for p in data[PIXELS]:
if not p in colours:
colours.append(p)
if len(colours) == 4:
break
# it's possible for the converted pixels to have less than 4 gray levels
colours = sorted(colours) # sort from low to high
# map each colour to e-paper gray indexes
# creates hex string of pixels
# e.g. "0033322222110200....", which is 4 level gray with 4bpp
if len(colours) == 1: # unlikely, but let's have this case here
pixels = data[PIXELS].replace(colours[0],BLACK)
elif len(colours) == 2: # black & white
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],WHITE)
elif len(colours) == 3:
pixels = data[PIXELS].replace(colours[0],DARK_GRAY)\
.replace(colours[1],GRAY)\
.replace(colours[2],WHITE)
else: # 4 grays as expected
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],DARK_GRAY)\
.replace(colours[2],GRAY)\
.replace(colours[3],WHITE)
# BMP pixel array starts from last row to first row
# and must be aligned to 4 bytes or 8 pixels
padding = "F" * ((BYTE/BPP) * ALIGNMENT - width % ((BYTE/BPP) * ALIGNMENT))
aligned_pixels = ''.join([pixels[i:i+width]+padding for i in range(0, len(pixels), width)][::-1])
# convert hex string to represented byte values
def Hex2Bytes(hexStr):
hexStr = ''.join(hexStr.split(" "))
bytes = []
for i in range(0, len(hexStr), 2):
byte = int(hexStr[i:i+2],16)
bytes.append(chr(byte))
return ''.join(bytes)
# convert integer to 4-byte little endian hex string
# e.g. 800 => 0x320 => 00000320 (big-endian) =>20030000 (little-endian)
def i2LeHexStr(i):
be_hex = ('0000000'+hex(i)[2:])[-8:]
n = 2 # split every 2 letters
return ''.join([be_hex[i:i+n] for i in range(0, len(be_hex), n)][::-1])
BMP_HEADER = BMP_HEADER % (i2LeHexStr(len(aligned_pixels)/(BYTE/BPP)+BMP_HEADER_SIZE),i2LeHexStr(width),i2LeHexStr(height))
sys.stdout.write(Hex2Bytes(BMP_HEADER+aligned_pixels))
编辑:有关此电子纸显示器的所有内容以及我在其上显示内容的代码都可以在此处找到:https://github.com/yy502/ePaperDisplay
解决问题做得很好。您还可以考虑为 ImageMagick 创建个人委托或自定义委托,以帮助自动化该过程。 ImageMagick 能够将它无法自行处理的格式委托给委托或助手,例如您的 2 位助手 ;-)
您可以在 $HOME/.magick/delegates.xml
中创建自己的代表,而不是干扰可能居住在 /etc/ImageMagick/delegates.xml
中的系统范围的代表。你的看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<delegatemap>
<delegate encode="epaper" command="convert "%f" +matte -colors 4 -depth 8 -colorspace gray pgm:- | /usr/local/bin/4_level_gray_4bpp_BMP_converter.py > out.bmp"/>
</delegatemap>
那么如果你 运行:
identify -list delegate
您会看到您的 "known" 助手。
这一切意味着您将能够 运行 命令,例如:
convert a.png epaper:
它会自动执行 2 位 BMP 操作。
你可以只使用这个:
convert in.jpg -colorspace gray +matte -colors 2 -depth 1 -resize '640x384>' pgm:- > out.bmp**
我也有这个电子纸显示器。经过大量的反复试验,我能够使用以下命令使用 ImageMagick 正确转换图像:
convert -verbose INPUT.BMP -resize 300x300 -monochrome -colorspace sRGB -colors 2 -depth 1 BMP3:OUTPUT.BMP
这对我适用于 Imagemagick 6.9.10.23 Q16 Mac OSX Sierra
输入:
convert logo.png -colorspace gray -depth 2 -type truecolor logo_depth8_gray_rgb.bmp
添加 -type truecolor 将图像转换为 RGB,但根据 -colorspace 灰色采用灰色调。并且深度 2 在直方图中仅创建 4 种颜色。
identify -verbose logo_depth8_gray_rgb.bmp
Image:
Filename: logo_depth8_gray_rgb.bmp
Format: BMP (Microsoft Windows bitmap image)
Class: DirectClass
Geometry: 640x480+0+0
Units: PixelsPerCentimeter
Colorspace: sRGB
Type: Grayscale
Base type: Undefined
Endianness: Undefined
Depth: 8/2-bit
Channel depth:
red: 2-bit
green: 2-bit
blue: 2-bit
Channel statistics:
Pixels: 307200
Red:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Green:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Blue:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Image statistics:
Overall:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29928
skewness: -2.38355
entropy: 0.417933
Colors: 4 <--------
Histogram: <--------
12730: (0,0,0) #000000 black
24146: (85,85,85) #555555 srgb(85,85,85)
9602: (170,170,170) #AAAAAA srgb(170,170,170)
260722: (255,255,255) #FFFFFF white
首先,我的问题与 How do I convert image to 2-bit per pixel? 不同,不幸的是,它的解决方案不适用于我的情况...
我需要将图像转换为每像素 2 位灰度 BMP 格式。示例图像具有以下属性:
Color Model: RGB
Depth: 4
Is Indexed: 1
Dimension: 800x600
Size: 240,070 bytes (4 bits per pixel but only last 2 bits are used to identify the gray scales as 0/1/2/3 in decimal or 0000/0001/0010/0011 in binary, plus 70 bytes BMP metadata or whatever)
示例BMP图像开头部分的十六进制值:
3代表图像开头的白色像素。再往下是一些代表黑色、深灰、浅灰的0、1、2:
使用下面的命令,
convert pic.png -colorspace gray +matte -depth 2 out.bmp
我可以在视觉上得到正确的 4 级灰度图像,但每个像素的深度或大小错误:
Color Model: RGB
Depth: 8 (expect 4)
Dimension: 800x504
Size: 1,209,738 bytes (something like 3 bytes per pixel, plus metadata)
(no mention of indexed colour space)
请帮忙...
看看 https://en.wikipedia.org/wiki/BMP_file_format#File_structure 。 问题是您没有指定颜色 table。根据维基百科文章,如果位深度小于 8 位,则这些是强制性的。
好的,我已经按照 Mark 的提示(请参阅原始问题下的评论)编写了一个 Python 脚本来手动创建具有 4bpp 的 4 级灰度 BMP。这种特定的BMP格式结构是针对微影的4.3英寸电子纸显示模块。规格可以在这里找到:http://www.waveshare.com/wiki/4.3inch_e-Paper
以下是将原始图像通过管道传输到我的代码并保存结果的方法。
convert in.png -colorspace gray +matte -colors 4 -depth 2 -resize '800x600>' pgm:- | ./4_level_gray_4bpp_BMP_converter.py > out.bmp
4_level_gray_4bpp_BMP_converter.py
的内容:
#!/usr/bin/env python
"""
### Sample BMP header structure, total = 70 bytes
### !!! little-endian !!!
Bitmap file header 14 bytes
42 4D "BM"
C6 A9 03 00 FileSize = 240,070 <= dynamic value
00 00 Reserved
00 00 Reserved
46 00 00 00 Offset = 70 = 14+56
DIB header (bitmap information header)
BITMAPV3INFOHEADER 56 bytes
28 00 00 00 Size = 40
20 03 00 00 Width = 800 <= dynamic value
58 02 00 00 Height = 600 <= dynamic value
01 00 Planes = 1
04 00 BitCount = 4
00 00 00 00 compression
00 00 00 00 SizeImage
00 00 00 00 XPerlPerMeter
00 00 00 00 YPerlPerMeter
04 00 00 00 Colours used = 4
00 00 00 00 ColorImportant
00 00 00 00 Colour definition index 0
55 55 55 00 Colour definition index 1
AA AA AA 00 Colour definition index 2
FF FF FF 00 Colour definition index 3
"""
# to insert File Size, Width and Height with hex strings in order
BMP_HEADER = "42 4D %s 00 00 00 00 46 00 00 00 28 00 00 00 %s %s 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 55 55 55 00 AA AA AA 00 FF FF FF 00"
BMP_HEADER_SIZE = 70
BPP = 4
BYTE = 8
ALIGNMENT = 4 # bytes per row
import sys
from re import findall
DIMENTIONS = 1
PIXELS = 3
BLACK = "0"
DARK_GRAY = "1"
GRAY = "2"
WHITE = "3"
# sample data:
# ['P5\n', '610 590\n', '255\n', '<1 byte per pixel for 4 levels of gray>']
# where item 1 is always P5, item 2 is width heigh, item 3 is always 255, items 4 is pixels/colours
data = sys.stdin.readlines()
width = int(data[DIMENTIONS].strip().split(' ')[0])
height = int(data[DIMENTIONS].strip().split(' ')[1])
if not width*height == len(data[PIXELS]):
print "Error: pixel data (%s bytes) and image size (%dx%d pixels) do not match" % (len(data[PIXELS]),width,height)
sys.exit()
colours = [] # enumerate 4 gray levels
for p in data[PIXELS]:
if not p in colours:
colours.append(p)
if len(colours) == 4:
break
# it's possible for the converted pixels to have less than 4 gray levels
colours = sorted(colours) # sort from low to high
# map each colour to e-paper gray indexes
# creates hex string of pixels
# e.g. "0033322222110200....", which is 4 level gray with 4bpp
if len(colours) == 1: # unlikely, but let's have this case here
pixels = data[PIXELS].replace(colours[0],BLACK)
elif len(colours) == 2: # black & white
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],WHITE)
elif len(colours) == 3:
pixels = data[PIXELS].replace(colours[0],DARK_GRAY)\
.replace(colours[1],GRAY)\
.replace(colours[2],WHITE)
else: # 4 grays as expected
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],DARK_GRAY)\
.replace(colours[2],GRAY)\
.replace(colours[3],WHITE)
# BMP pixel array starts from last row to first row
# and must be aligned to 4 bytes or 8 pixels
padding = "F" * ((BYTE/BPP) * ALIGNMENT - width % ((BYTE/BPP) * ALIGNMENT))
aligned_pixels = ''.join([pixels[i:i+width]+padding for i in range(0, len(pixels), width)][::-1])
# convert hex string to represented byte values
def Hex2Bytes(hexStr):
hexStr = ''.join(hexStr.split(" "))
bytes = []
for i in range(0, len(hexStr), 2):
byte = int(hexStr[i:i+2],16)
bytes.append(chr(byte))
return ''.join(bytes)
# convert integer to 4-byte little endian hex string
# e.g. 800 => 0x320 => 00000320 (big-endian) =>20030000 (little-endian)
def i2LeHexStr(i):
be_hex = ('0000000'+hex(i)[2:])[-8:]
n = 2 # split every 2 letters
return ''.join([be_hex[i:i+n] for i in range(0, len(be_hex), n)][::-1])
BMP_HEADER = BMP_HEADER % (i2LeHexStr(len(aligned_pixels)/(BYTE/BPP)+BMP_HEADER_SIZE),i2LeHexStr(width),i2LeHexStr(height))
sys.stdout.write(Hex2Bytes(BMP_HEADER+aligned_pixels))
编辑:有关此电子纸显示器的所有内容以及我在其上显示内容的代码都可以在此处找到:https://github.com/yy502/ePaperDisplay
解决问题做得很好。您还可以考虑为 ImageMagick 创建个人委托或自定义委托,以帮助自动化该过程。 ImageMagick 能够将它无法自行处理的格式委托给委托或助手,例如您的 2 位助手 ;-)
您可以在 $HOME/.magick/delegates.xml
中创建自己的代表,而不是干扰可能居住在 /etc/ImageMagick/delegates.xml
中的系统范围的代表。你的看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<delegatemap>
<delegate encode="epaper" command="convert "%f" +matte -colors 4 -depth 8 -colorspace gray pgm:- | /usr/local/bin/4_level_gray_4bpp_BMP_converter.py > out.bmp"/>
</delegatemap>
那么如果你 运行:
identify -list delegate
您会看到您的 "known" 助手。
这一切意味着您将能够 运行 命令,例如:
convert a.png epaper:
它会自动执行 2 位 BMP 操作。
你可以只使用这个:
convert in.jpg -colorspace gray +matte -colors 2 -depth 1 -resize '640x384>' pgm:- > out.bmp**
我也有这个电子纸显示器。经过大量的反复试验,我能够使用以下命令使用 ImageMagick 正确转换图像:
convert -verbose INPUT.BMP -resize 300x300 -monochrome -colorspace sRGB -colors 2 -depth 1 BMP3:OUTPUT.BMP
这对我适用于 Imagemagick 6.9.10.23 Q16 Mac OSX Sierra
输入:
convert logo.png -colorspace gray -depth 2 -type truecolor logo_depth8_gray_rgb.bmp
添加 -type truecolor 将图像转换为 RGB,但根据 -colorspace 灰色采用灰色调。并且深度 2 在直方图中仅创建 4 种颜色。
identify -verbose logo_depth8_gray_rgb.bmp
Image:
Filename: logo_depth8_gray_rgb.bmp
Format: BMP (Microsoft Windows bitmap image)
Class: DirectClass
Geometry: 640x480+0+0
Units: PixelsPerCentimeter
Colorspace: sRGB
Type: Grayscale
Base type: Undefined
Endianness: Undefined
Depth: 8/2-bit
Channel depth:
red: 2-bit
green: 2-bit
blue: 2-bit
Channel statistics:
Pixels: 307200
Red:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Green:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Blue:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Image statistics:
Overall:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29928
skewness: -2.38355
entropy: 0.417933
Colors: 4 <--------
Histogram: <--------
12730: (0,0,0) #000000 black
24146: (85,85,85) #555555 srgb(85,85,85)
9602: (170,170,170) #AAAAAA srgb(170,170,170)
260722: (255,255,255) #FFFFFF white