2016-03-04 4 views
3

Прежде всего, мой вопрос отличается от How do I convert image to 2-bit per pixel? и, к сожалению, его решения не работает в моем случае ...преобразования изображений в индексированные 2-битных оттенки серого BMP

мне нужно конвертировать изображения в 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) 

sample

Значение Hex из начальной части образца BMP изображения: head of sample image

В 3s представляет собой белые пиксели в начале изображения , Дальше есть несколько 0s, 1s и 2s, представляющего черные, темно-серые и светло-серый: mid-part of sample image

С ниже командой,

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) 

conversion outcome

Пожалуйста, помогите ...

+0

Какая ужасная программа требует использовать 2 bpp? ImageMagick не поддерживает это. NetPBM не поддерживает это. Википедия говорит * «Типичные значения: 1, 4, 8, 16, 24 и 32» * здесь https://en.wikipedia.org/wiki/BMP_file_format –

+0

это модуль отображения электронной бумаги, который поддерживает 4 уровня серого дисплея , его производство обеспечивает конвертер изображений для платформы Windows, но я - пользователь Linux/Mac. – yy502

+0

Возможно, попробуйте запустить свою программу в VirtualBox - это бесплатно и позволит вам запускать Windows на своих Mac и Linux. Или попробуйте «вино». Можете ли вы предоставить название продукта и ссылку на производителя модуля, и если идет дождь или мне становится скучно, я могу написать версию для Mac/Linux. Однако никаких обещаний. –

ответ

3

ОК, я написал сценарий Python, следуя подсказкам Марка (см. Комментарии по оригинальному вопросу), чтобы вручную создать 4-уровневый BMP с 6-битным уровнем масштабирования с 4bpp. Эта конкретная конструкция формата BMP предназначена для 4,3-дюймового модуля отображения электронной бумаги, созданного WaveShare. Спекуляции можно найти здесь: 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)) 

Edit: все об этой электронной бумаги дисплей и мой код, чтобы показать вещи на нем можно найти здесь: https://github.com/yy502/ePaperDisplay

enter image description here

+0

Молодцы! И спасибо за обмен с сообществом. Возможно, добавьте несколько слов с маркой и моделью устройства, чтобы другие пользователи нашли ваш ответ при поиске. Молодец! –

+0

Готово. Спасибо за предложение! :-) – yy502

0

Посмотрите на https://en.wikipedia.org/wiki/BMP_file_format#File_structure. Проблема в том, что вы не указываете таблицу цветов. Согласно статье wikipedia, они являются обязательными, если глубина бит меньше 8 бит.

+0

Большое спасибо за ваш ответ. вы правы, образец BMP индексируется, в то время как мой результат не упоминает «индексированный» в свойствах. Наверное, мне нужно сосредоточиться на преобразовании оригинала в индексированное изображение. – yy502

+0

ОК, кажется, быстрее, если я написал свой собственный скрипт, чтобы создать формат изображения BMP, который мне нужен, используя необработанные байты. спасибо за диаграмму структуры файла! – yy502

0

Хорошо сделано по решению проблемы. Вы можете подумать о том, чтобы сделать персональный делегат или пользовательский делегат для ImageMagick, чтобы помочь автоматизировать процесс. ImageMagick может делегировать форматы, которые он не может обработать сам делегатам или помощникам, таким как ваш двухбитовый помощник ;-)

Вместо того, чтобы вмешиваться в общесистемные делегаты, которые, вероятно, живут в /etc/ImageMagick/delegates.xml, вы можете сделать свой собственный в $HOME/.magick/delegates.xml.Ваш будет выглядеть примерно так:

<?xml version="1.0" encoding="UTF-8"?> 
<delegatemap> 
    <delegate encode="epaper" command="convert &quot;%f&quot; +matte -colors 4 -depth 8 -colorspace gray pgm:- | /usr/local/bin/4_level_gray_4bpp_BMP_converter.py > out.bmp"/> 
</delegatemap> 

Тогда при запуске:

identify -list delegate 

вы увидите ваш перечислен как «известных» помощника.

Все это означает, что вы будете иметь возможность запускать команды, как:

convert a.png epaper: 

и он будет делать 2-битный BMP вещь автомагически.

+0

Прохладный! Это доводит его до следующего уровня! Я бы с удовольствием отдам это :-) – yy502

+0

Я собираюсь поместить весь код здесь https://github.com/yy502/ePaperDisplay и постепенно убирать их. – yy502

+0

Прохладный. Нажмите «поделиться» под своим ответом здесь, в StackOverflow, и он предоставит вам URL-адрес, который вы можете скопировать и вставить в репозиторий GitHub. –

Смежные вопросы