Использование numpy и pil для преобразования 565 (16-битный цвет) в 888 (24-битный цвет)

Я должен предварить это тем фактом, что у меня есть рабочий метод, использующий битовый сдвиг и путпиксель, но он невероятно медленный, и я хочу использовать numpy для ускорения процесса. Я верю, что я близко, но не совсем там. Рассчитав то, что, по моему мнению, должно сработать, я вижу улучшение времени на 0,3 секунды, отсюда и моя мотивация.

Текущий рабочий код:

buff # a binary set of data
im = Image.new("RGBA",(xdim,ydim))
for y in xrange(ydim):
    for x in xrange(xdim):
        px = buff[x*y]
        # the 255 is for the alpha channel which I plan to use later
        im.putpixel((x,y),(px&0xF800) >> 8, (px&0x07E0) >> 3, (px&0x001F) <<3, 255))
return im

Код, который я пытаюсь заставить работать, выглядит так:

im16 = numpy.fromstring(buff,dtype=numpy.uint16) #read data as shorts
im16 = numpy.array(im16,dtype=numpy.uint32) #now that it's in the correct order, convert to 32 bit so there is room to do shifting
r    = numpy.right_shift(8, im16.copy() & 0xF800)
g    = numpy.right_shift(3, im16.copy() & 0x07E0)
b    = numpy.left_shift( 3, im16 & 0x001F)
pA   = numpy.append(r,g)
pB   = numpy.append(b,numpy.ones((xdim,ydim),dtype=numpy.uint32) * 0xFF) #this is a black alpha channel
img  = numpy.left_shift(img,8) #gives me green channel
im24 = Image.fromstring("RGBA",(xdim,ydim),img)
return im24

Итак, последняя проблема заключается в том, что каналы не объединяются, и я не думаю, что мне придется делать этот окончательный сдвиг битов (обратите внимание, что я получаю красный канал, если я не сдвигаю биты на 8). Будем очень признательны за помощь в том, как правильно все совместить.

РЕШЕНИЕ

import numpy as np
arr = np.fromstring(buff,dtype=np.uint16).astype(np.uint32)
arr = 0xFF000000 + ((arr & 0xF800) >> 8) + ((arr & 0x07E0) << 5) + ((arr & 0x001F) << 19)
return Image.frombuffer('RGBA', (xdim,ydim), arr, 'raw', 'RGBA', 0, 1)

разница в том, что вам нужно запаковать его как счетчик MSB(ALPHA,B,G,R)LSB, интуитивно понятный из putpixel, но он работает, и работает хорошо


person pyInTheSky    schedule 24.03.2011    source источник


Ответы (2)


Предупреждение: следующий код не проверялся, но я думаю, что это сделает то, что вы хотите (если я все правильно понимаю):

import numpy as np
arr = np.fromstring(buff,dtype=np.uint16).astype(np.uint32)
arr = ((arr & 0xF800) << 16) + ((arr & 0x07E0) << 13) + ((arr & 0x001F) << 11) + 0xFF
return Image.frombuffer('RGBA', (xdim,ydim), arr, 'raw', 'RGBA', 0, 1)

Я объединяю все каналы вместе в 32-битную линию, которая выполняет весь битовый сдвиг. Крайние левые 8 бит — красные, следующие 8 — зеленые, следующие 8 — синие и последние 8 — альфа. Цифры сдвига могут показаться немного странными, потому что я включил сдвиги из 16-битного формата. Кроме того, я использую frombuffer, потому что тогда мы хотим воспользоваться преимуществами буфера, используемого Numpy, а не сначала преобразовывать в строку.

Может быть полезно взглянуть на эту страницу. На мой взгляд, это не очень здорово, но, по моему опыту, так обстоят дела с PIL. Документация на самом деле не очень удобна для пользователя, на самом деле я часто нахожу ее запутанной, но я не собираюсь добровольно переписывать ее, потому что я мало использую PIL.

person Justin Peel    schedule 24.03.2011
comment
Спасибо за быстрый ответ. Это выглядит правильно, но когда я попробовал, это не сработало правильно. Я вставил некоторые операторы отладки... начало буфера выглядит как ['\x10\x00\x0b\xe5'] ... теперь я сделал дамп "короткого" буфера в буфер "char", так что порядок следования байтов получил перевернутый. Тем не менее, преобразование, кажется, сработало в мою пользу, поскольку я посмотрел на arr[0] => \x10L и arr[1] => \xe50b (это должно быть после второй строки в вашем фрагменте. Что действительно озадачивает, так это то, что после 3-я строка, я снова посмотрел на arr[0] и с удивлением увидел 0x0L... по крайней мере, я ожидал 0xFF. - person pyInTheSky; 24.03.2011
comment
Интересно, похоже, проблема заключалась и в заключении ‹‹ в круглые скобки. Оператор «+», кажется, имеет приоритет! - person pyInTheSky; 24.03.2011
comment
Все выглядит хорошо, за исключением того, что по какой-то причине цвета выключены. Я просмотрел документацию PIL, но не видел списка того, что вы можете заменить «сырым» или 0,1 на .. я предполагаю, что 0,1 должен делать с позицией, но есть ли что-нибудь, что вы можете думаете о том, что вызовет причудливые цвета? .. Спасибо :) (Я экспериментировал со всеми комбинациями битового сдвига, которые только мог придумать) - person pyInTheSky; 24.03.2011
comment
Странно, что знаки ‹‹ тоже должны быть в круглых скобках, но я их вставил. ="nofollow noreferrer">docs.python.org/reference/expressions.html#summary Думаю, это имеет смысл. Я не осознавал, что приоритет операторов Python отличается от приоритета операторов C, но опять же, я не очень часто их использую. - person Justin Peel; 24.03.2011
comment
Я не удивлен, что цвета странные. Вы берете числа на одной шкале и помещаете эти значения только в верхние части другой шкалы. Другими словами, вам нужно масштабировать значения. Вам, вероятно, понадобится плавающая запятая, чтобы получить ее очень точно. Возможно, вы захотите, чтобы PIL выполнил преобразование для вас, как в этом вопросе: stackoverflow.com/questions/4199497/, но если вы продолжите то, что мы делали, у вас будет больше контроля. - person Justin Peel; 24.03.2011
comment
так близко, наконец-то получил решение (опубликовано в исходном посте) большое спасибо за помощь - person pyInTheSky; 25.03.2011
comment
Рад, что смог помочь. Это странно, но, по крайней мере, вы решили это. - person Justin Peel; 25.03.2011

Если вы хотите выполнить масштабирование надлежащим образом, вот более PIL-иш для решения вашей проблемы.

FROM_5 = ((np.arange(32, dtype=numpy.uint16) * 255 + 15) // 31).astype(numpy.ubyte)
FROM_6 = ((np.arange(64, dtype=numpy.uint16) * 255 + 31) // 63).astype(numpy.ubyte)

data = numpy.fromstring(buff, dtype=numpy.uint16)
r = Image.frombuffer('L', shape, FROM_5[data >> 11], 'raw', 'L', 0, 1)
g = Image.frombuffer('L', shape, FROM_6[(data >> 5) & 0x3F], 'raw', 'L', 0, 1)
b = Image.frombuffer('L', shape, FROM_5[data & 0x1F], 'raw', 'L', 0, 1)
return Image.merge('RGB', (r, g, b))
person Scott David Daniels    schedule 22.06.2011
comment
Я не верю, что опубликованное «решение» имеет проблему масштабирования. Не могли бы вы указать, где существует недостаток? Спасибо : ) - person pyInTheSky; 28.06.2011