将图像转换为索引的2位灰度BMP [英] converting images to indexed 2-bit grayscale BMP

查看:65
本文介绍了将图像转换为索引的2位灰度BMP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我的问题不同于

样本BMP图像开始部分的十六进制值:

3表示图像开始处的白色像素.再往下是代表黑色,深灰色和浅灰色的0、1和2:

使用以下命令,

 转换pic.png -colorspace灰色+ matte -depth 2 out.bmp 

我可以在视觉上获得 正确的4级灰度图像,但是每像素的深度或大小不正确:

 颜色型号:RGB深度:8(期望值4)尺寸:800x504大小:1,209,738字节(大约每个像素3字节,外加元数据)(未提及索引色彩空间) 

请帮助...

解决方案

好的,我已经按照Mark的提示编写了Python脚本(请参阅原始问题下的注释),以手动创建具有4bpp的4级灰度BMP.这种特定的BMP格式构造适用于WaveShare制造的4.3英寸电子纸显示模块.可以在这里找到规格:

First of all, my question is different to How do I convert image to 2-bit per pixel? and unfortunately its solution does not work in my case...

I need to convert images to 2-bit per pixel grayscale BMP format. The sample image has the following properties:

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)

The Hex values of the beginning part of the sample BMP image:

The 3s represent white pixels at the beginning of the image. Further down there are some 0s, 1s and 2s representing black, dark gray and light gray:

With the command below,

convert pic.png -colorspace gray +matte -depth 2 out.bmp

I can get visually correct 4-level grayscale image, but wrong depth or size per pixel:

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)

Please help...

解决方案

OK, I have written a Python script following Mark's hints (see comments under original question) to manually create a 4-level gray scale BMP with 4bpp. This specific BMP format construction is for the 4.3 inch e-paper display module made by WaveShare. Specs can be found here: http://www.waveshare.com/wiki/4.3inch_e-Paper

Here's how to pipe the original image to my code and save the outcome.

convert in.png -colorspace gray +matte -colors 4 -depth 2 -resize '800x600>' pgm:- | ./4_level_gray_4bpp_BMP_converter.py > out.bmp

Contents of 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))

Edit: everything about this e-paper display and my code to display things on it can be found here: https://github.com/yy502/ePaperDisplay

这篇关于将图像转换为索引的2位灰度BMP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆