преобразование изображений в индексированный 2-битный BMP с оттенками серого

Прежде всего, мой вопрос отличается от Как мне преобразовать изображение в 2 бита на пиксель? и, к сожалению, его решение не работает в моем случае ...

Мне нужно преобразовать изображения в формат BMP с оттенками серого 2 бита на пиксель. Образец изображения имеет следующие свойства:

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 представляют собой белые пиксели в начале изображения. Ниже находятся несколько нулей, единиц и двоек, представляющих черный, темно-серый и светло-серый: средняя часть образца изображения

С помощью приведенной ниже команды

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

Я могу получить визуально правильное четырехуровневое изображение в оттенках серого, но неправильную глубину или размер на пиксель:

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)

результат конверсии

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


person yy502    schedule 04.03.2016    source источник
comment
Какая ужасная программа требует от вас использовать 2 бит на пиксель? ImageMagick этого не поддерживает. NetPBM этого не поддерживает. Википедия говорит: Типичные значения: 1, 4, 8, 16, 24 и 32 здесь en.wikipedia.org/wiki/BMP_file_format   -  person Mark Setchell    schedule 04.03.2016
comment
Он предназначен для модуля отображения электронной бумаги, который изначально поддерживает четырехуровневое отображение серой шкалы. его производитель предоставляет только конвертер изображений для платформы Windows, но я пользователь Linux / Mac.   -  person yy502    schedule 04.03.2016
comment
Возможно, попробуйте запустить их программу в VirtualBox - это бесплатно и позволит вам запускать Windows на вашем Mac и Linux. Или попробуйте wine. Можете ли вы предоставить название продукта и ссылку на производителя модуля, и, если пойдет дождь или мне станет скучно, я могу написать версию для Mac / Linux. Но никаких обещаний.   -  person Mark Setchell    schedule 04.03.2016
comment
Или, если вы не хотите загрязнять свой любимый Mac «мусором Windows», вы можете запустить бесплатный Windows Amazon E2C и установить там конвертер :-)   -  person Mark Setchell    schedule 04.03.2016
comment
Кстати, вы можете заставить ImageMagick создать BMP с палитрой / индексированием, добавив -type palette в вашу команду.   -  person Mark Setchell    schedule 04.03.2016
comment
привет Марк, спасибо за комментарии. У меня есть Windows в виртуальной машине для определенных приложений, но я большой фанат сценариев и автоматизации, и для эффективности я предпочитаю не графический интерфейс :-) Конвертер Windows BMP заархивирован в 7zip как UC-GUI-BitmapConvert.7z на waveshare.com/wiki/4.3inch_e-Paper_Software Это единственный исполняемый файл, который открывает существующий BMP, выберите «Конвертировать-› 2bpp »и сохраните в желаемом формате для модуля электронной бумаги. Я написал библиотеку Python для обновления электронного документа с помощью cmdline вместо утилиты с графическим интерфейсом. Как только я преодолею это препятствие, все будет в командной строке.   -  person yy502    schedule 05.03.2016
comment
просто попробовал -type palette, не сработало. все еще получаю 3 байта на пиксель.   -  person yy502    schedule 05.03.2016
comment
Думаю, я собираюсь вручную собрать BMP-файлы размером 2bpp по байтам. как только я создаю заголовок файла BMP, следуя en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header преобразовать остальные пиксели в двоичный файл легко.   -  person yy502    schedule 05.03.2016
comment
Круто, удачи! Не забудьте поделиться им с ответом, и тогда вы сможете их принять. Подумайте о том, чтобы позволить ImageMagick выполнять уменьшение глубины, преобразование шкалы серого, удаление альфа-канала и преобразование в 8-битный формат, чтобы ваша программа была очень простой ... convert input.png +matte -colors 4 -depth 8 -colorspace gray pgm:- | yourProgram > 2bpp.bmp. Или то же самое, но с более легким NetPBM pngtopam input.png | pamcolors 4 ... | yourProgram.   -  person Mark Setchell    schedule 05.03.2016
comment
stackoverflow.com/a/2654860/2836621 ... может служить отправной точкой.   -  person Mark Setchell    schedule 05.03.2016
comment
Сделанный. Спасибо за вдохновение! :-)   -  person yy502    schedule 07.03.2016


Ответы (6)


Хорошо, я написал сценарий Python, следуя подсказкам Марка (см. Комментарии к исходному вопросу), чтобы вручную создать четырехуровневый BMP с серой шкалой с 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))

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

введите описание изображения здесь

person yy502    schedule 06.03.2016
comment
Отличная работа! И спасибо, что поделился с сообществом. Может быть, добавьте пару слов о марке и модели устройства, чтобы другие пользователи нашли ваш ответ при поиске. И снова молодцы! - person Mark Setchell; 07.03.2016
comment
Сделанный. Спасибо за предложение! :-) - person yy502; 07.03.2016

Это работает для меня в 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 gray. А глубина 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
person fmw42    schedule 05.01.2019
comment
Когда я пытаюсь использовать это в Linux, я получаю: biBitCount = 24 и Debug: Bmp image is not a 4-color bitmap!, поэтому в моем случае это не работает. Может ли ОС иметь значение? ` - person Sjoerd222888; 04.10.2020
comment
Какая у вас версия IM? Можете ли вы опубликовать свое входное и выходное изображение? - person fmw42; 04.10.2020

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

person Aziuth    schedule 04.03.2016
comment
Большое спасибо за ваш ответ. вы правы, образец BMP проиндексирован, в то время как в моем результате в свойствах не упоминается «проиндексировано». Думаю, мне нужно как-то сосредоточиться на преобразовании оригинала в индексированное изображение. - person yy502; 04.03.2016
comment
хорошо, кажется, быстрее, если я написал свой собственный сценарий для создания формата изображения BMP, который мне нужен, используя необработанные байты. спасибо за схему файловой структуры! - person yy502; 05.03.2016

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

Вместо того, чтобы мешать общесистемным делегатам, которые, вероятно, находятся в /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 автоматически.

person Mark Setchell    schedule 07.03.2016
comment
Прохладный! Это выводит его на новый уровень! Я бы хотел попробовать :-) - person yy502; 07.03.2016
comment
Я собираюсь поместить сюда весь код github.com/yy502/ePaperDisplay и постепенно приводить в порядок их . - person yy502; 07.03.2016
comment
Прохладный. Щелкните share под своим ответом здесь, в StackOverflow, и он предоставит вам URL-адрес, который вы можете скопировать и вставить в репозиторий GitHub. - person Mark Setchell; 07.03.2016

Вы можете просто использовать это:

convert in.jpg -colorspace gray +matte -colors 2 -depth 1 -resize '640x384>' pgm:- > out.bmp**
person Milan Jurkulak    schedule 21.09.2018

У меня тоже есть этот дисплей электронной бумаги. После множества проб и ошибок я смог правильно преобразовать изображения с помощью ImageMagick, используя следующую команду:

convert -verbose INPUT.BMP -resize 300x300 -monochrome -colorspace sRGB -colors 2 -depth 1 BMP3:OUTPUT.BMP
person johnpm    schedule 05.01.2019