Найти - Пользователи
Полная версия: Запись и чтение PIL объекта.
Начало » Python для новичков » Запись и чтение PIL объекта.
1 2
nauman
Перерыл “весь” интернет, вопрос остался открытым. Ошибка встречается часто, конкретных ответов на нее не нашел.
Python 2.6, win32. Есть следующий фрагмент кода с выводом ошибки:
from PIL import ImageGrab, Image
im_size = ImageGrab.grab().size
im = ImageGrab.grab().quantize(colors=96, method=1)
to_string = im.tostring()
from_string = Image.fromstring("RGBA", im_size, to_string, "raw", "F;16")

Traceback (most recent call last):
File "C:\Python26\a", line 7, in <module>
from_string = Image.fromstring("RGBA", im_size, to_string, "raw", "F;16")
File "C:\Python26\lib\site-packages\PIL\Image.py", line 1797, in fromstring
im.fromstring(data, decoder_name, args)
File "C:\Python26\lib\site-packages\PIL\Image.py", line 589, in fromstring
d = _getdecoder(self.mode, decoder_name, args)
File "C:\Python26\lib\site-packages\PIL\Image.py", line 383, in _getdecoder
return apply(decoder, (mode,) + args + extra)
ValueError: unknown raw mode
Может кто сталкивался с этой проблемой, как ее победить?
Андрей Светлов
А отчего вы решили, что mode “RGBA” имеет raw mode “F;16”?
И, кстати, что это означает с вашей точки зрения?

P.S. Зачем вам “весь интернет”, когда PIL поставляется с исходниками? :)
nauman
В интернете я искал ответ именно на эту ошибку, но не нашел, точнее проблема есть, а ответов нет. Почти вся документация на английском, далеко не все удается понять.
Если бы я знал, что это означает, тут вопросы не задавал бы. Если у вас есть ответ, то прошу помочь.
Андрей Светлов
Я спрашивал, чтобы понять - насколько подробно нужно объяснять.
Документация по PIL, кстати, откровенно хреновая - поэтому и рекомендую читать исходники. Они подробные и не врут.

Полностью описывать все не буду - очень длинно выйдет. Если будут вопросы - задавайте.

PIL.Image хранит картинку в raw формате (развернутом). Всякие TIFF и JPEG перекодируются в raw и обратно при чтении-записи изображения.
Поэтому остановимся именно на raw.

Сначала приведу полную таблицу поддерживаемых режимов, а потом расскажу как ее читать.
Это - кусочек из libimaging/Unpack.c
    /* raw mode syntax is "<mode>;<bits><flags>" where "bits" defaults
depending on mode (1 for "1", 8 for "P" and "L", etc), and
"flags" should be given in alphabetical order. if both bits
and flags have their default values, the ; should be left out */

/* flags: "I" inverted data; "R" reversed bit order; "B" big
endian byte order (default is little endian); "L" line
interleave, "S" signed, "F" floating point */

/* bilevel */
{"1", "1", 1, unpack1},
{"1", "1;I", 1, unpack1I},
{"1", "1;R", 1, unpack1R},
{"1", "1;IR", 1, unpack1IR},

/* greyscale */
{"L", "L;2", 2, unpackL2},
{"L", "L;4", 4, unpackL4},
{"L", "L", 8, copy1},
{"L", "L;I", 8, unpackLI},
{"L", "L;R", 8, unpackLR},
{"L", "L;16", 16, unpackL16},
{"L", "L;16B", 16, unpackL16B},

/* greyscale w. alpha */
{"LA", "LA", 16, unpackLA},
{"LA", "LA;L", 16, unpackLAL},

/* palette */
{"P", "P;1", 1, unpackP1},
{"P", "P;2", 2, unpackP2},
{"P", "P;2L", 2, unpackP2L},
{"P", "P;4", 4, unpackP4},
{"P", "P;4L", 4, unpackP4L},
{"P", "P", 8, copy1},
{"P", "P;R", 8, unpackLR},

/* palette w. alpha */
{"PA", "PA", 16, unpackLA},
{"PA", "PA;L", 16, unpackLAL},

/* true colour */
{"RGB", "RGB", 24, ImagingUnpackRGB},
{"RGB", "RGB;L", 24, unpackRGBL},
{"RGB", "RGB;R", 24, unpackRGBR},
{"RGB", "RGB;16B", 48, unpackRGB16B},
{"RGB", "BGR", 24, ImagingUnpackBGR},
{"RGB", "BGR;15", 16, ImagingUnpackBGR15},
{"RGB", "BGR;16", 16, ImagingUnpackBGR16},
{"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
{"RGB", "RGBX", 32, copy4},
{"RGB", "RGBX;L", 32, unpackRGBAL},
{"RGB", "BGRX", 32, ImagingUnpackBGRX},
{"RGB", "XRGB", 24, ImagingUnpackXRGB},
{"RGB", "XBGR", 32, ImagingUnpackXBGR},
{"RGB", "YCC;P", 24, ImagingUnpackYCC},
{"RGB", "R", 8, band0},
{"RGB", "G", 8, band1},
{"RGB", "B", 8, band2},

/* true colour w. alpha */
{"RGBA", "LA", 16, unpackRGBALA},
{"RGBA", "LA;16B", 32, unpackRGBALA16B},
{"RGBA", "RGBA", 32, copy4},
{"RGBA", "RGBa", 32, unpackRGBa},
{"RGBA", "RGBA;I", 32, unpackRGBAI},
{"RGBA", "RGBA;L", 32, unpackRGBAL},
{"RGBA", "RGBA;16B", 64, unpackRGBA16B},
{"RGBA", "BGRA", 32, unpackBGRA},
{"RGBA", "ARGB", 32, unpackARGB},
{"RGBA", "ABGR", 32, unpackABGR},
{"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},
{"RGBA", "R", 8, band0},
{"RGBA", "G", 8, band1},
{"RGBA", "B", 8, band2},
{"RGBA", "A", 8, band3},

/* true colour w. padding */
{"RGBX", "RGB", 24, ImagingUnpackRGB},
{"RGBX", "RGB;L", 24, unpackRGBL},
{"RGBX", "RGB;16B", 48, unpackRGB16B},
{"RGBX", "BGR", 24, ImagingUnpackBGR},
{"RGBX", "BGR;15", 16, ImagingUnpackBGR15},
{"RGB", "BGR;16", 16, ImagingUnpackBGR16},
{"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
{"RGBX", "RGBX", 32, copy4},
{"RGBX", "RGBX;L", 32, unpackRGBAL},
{"RGBX", "BGRX", 32, ImagingUnpackBGRX},
{"RGBX", "XRGB", 24, ImagingUnpackXRGB},
{"RGBX", "XBGR", 32, ImagingUnpackXBGR},
{"RGBX", "YCC;P", 24, ImagingUnpackYCC},
{"RGBX", "R", 8, band0},
{"RGBX", "G", 8, band1},
{"RGBX", "B", 8, band2},
{"RGBX", "X", 8, band3},

/* colour separation */
{"CMYK", "CMYK", 32, copy4},
{"CMYK", "CMYK;I", 32, unpackCMYKI},
{"CMYK", "CMYK;L", 32, unpackRGBAL},
{"CMYK", "C", 8, band0},
{"CMYK", "M", 8, band1},
{"CMYK", "Y", 8, band2},
{"CMYK", "K", 8, band3},
{"CMYK", "C;I", 8, band0I},
{"CMYK", "M;I", 8, band1I},
{"CMYK", "Y;I", 8, band2I},
{"CMYK", "K;I", 8, band3I},

/* video (YCbCr) */
{"YCbCr", "YCbCr", 24, ImagingUnpackRGB},
{"YCbCr", "YCbCr;L", 24, unpackRGBL},
{"YCbCr", "YCbCrX", 32, copy4},
{"YCbCr", "YCbCrK", 32, copy4},

/* integer variations */
{"I", "I", 32, copy4},
{"I", "I;8", 8, unpackI8},
{"I", "I;8S", 8, unpackI8S},
{"I", "I;16", 16, unpackI16},
{"I", "I;16S", 16, unpackI16S},
{"I", "I;16B", 16, unpackI16B},
{"I", "I;16BS", 16, unpackI16BS},
{"I", "I;16N", 16, unpackI16N},
{"I", "I;16NS", 16, unpackI16NS},
{"I", "I;32", 32, unpackI32},
{"I", "I;32S", 32, unpackI32S},
{"I", "I;32B", 32, unpackI32B},
{"I", "I;32BS", 32, unpackI32BS},
{"I", "I;32N", 32, unpackI32N},
{"I", "I;32NS", 32, unpackI32NS},

/* floating point variations */
{"F", "F", 32, copy4},
{"F", "F;8", 8, unpackF8},
{"F", "F;8S", 8, unpackF8S},
{"F", "F;16", 16, unpackF16},
{"F", "F;16S", 16, unpackF16S},
{"F", "F;16B", 16, unpackF16B},
{"F", "F;16BS", 16, unpackF16BS},
{"F", "F;16N", 16, unpackF16N},
{"F", "F;16NS", 16, unpackF16NS},
{"F", "F;32", 32, unpackF32},
{"F", "F;32S", 32, unpackF32S},
{"F", "F;32B", 32, unpackF32B},
{"F", "F;32BS", 32, unpackF32BS},
{"F", "F;32N", 32, unpackF32N},
{"F", "F;32NS", 32, unpackF32NS},
{"F", "F;32F", 32, unpackF32F},
{"F", "F;32BF", 32, unpackF32BF},
{"F", "F;32NF", 32, unpackF32NF},
#ifdef FLOAT64
{"F", "F;64F", 64, unpackF64F},
{"F", "F;64BF", 64, unpackF64BF},
{"F", "F;64NF", 64, unpackF64NF},
#endif

/* storage modes */
{"I;16", "I;16", 16, copy2},
{"I;16B", "I;16B", 16, copy2},
{"I;16L", "I;16L", 16, copy2},

{NULL} /* sentinel */
};
Первая колонка - логический режим.
1 - битовая маска, 0 или 1
L - градация серого (greyscale)
LA - серый с прозрачностью (alpha)
P - палитровый режим
PA - он же с альфой
RGB - классические три цвета
RGBA - с альфой
RGBX - с выравниванием (чтобы добить до четного числа байт)
CMYK - есть такой типографский способ кодирования цвета
YCbCr - используется для видео
I - в душЕ похож на grayscale, только диапазон шире
F - то же что и I, но используются вещественные числа в отличие от целых

Сколько заглавных букв/цифр - столько каналов.
Например, в RGBA - четыре канала. Красный, зеленый, синий и прозрачность.
RGB - то же но без прозрачности.
F - картинка всего в один канал, зато он float. Т.е. можно писать числа в очень широком диапазоне.
Например, с пересветом (больше единицы - все равно белый, зато при дальнейшей обработке может стать важным, насколько таки - белый, или слегка серенький все же).

Третья колонка - сколько бит занимает пиксель.
Четвертая - функция кодирования, сейчас она не важна.

Вторая колонка - raw mode.
Описывает физический порядок записи в памяти.
В начале таблицы есть комментарий, разъясняющий принцип кодирования.
Дело в том, что RGB можно записать по разному.

Например, именно как RGB - по байту на красный, зеленый и синий.
Или как BGR - тоже популярная кодировка, красный и синий в памяти переставлены местами.
Или RGBX - последний байт может быть мусором или альфой, но нам он не интересен, игнорируем.
Или даже как RGB;16B - довольно популярный формат, некоторые сканеры умеют давать такую картинку.
По два байта на канал, при этом они в big endian - биты зеркально переставлены. Цвет задается в разы точнее - сами понимаете.

Теперь возвращаемся к вашим баранам.
ImageGrab.grab() - делает скриншот. Для скорости стоит вызывать его только раз - затратная операция каждый раз делать копию экрана ;)
Кстати, у этой картинки mode будет RGB (зачем для скриншота альфа?), а raw mode - BGR (так уж в bmp принято).
Вам raw mode в данном случае не интересен - ведь есть нормальный Image и все тут.
Правда, существует ньюанс - пока raw mode у исходной и результирующей картинки один и тот же - затратные преобразования битов делать не нужно, все просто летает.

Затем вы делаете .tostring()
возвращаемый буфер - это именно битовое представление о картинке. Без информации о том, как именно ее следует читать - просто серия байт.
Чтобы затянуть эту мешанину обратно через .fromstring(), нужно правильно указать ее raw mode - иначе все перепутается, синий станет красным и т.д. даже если общие размеры совпадут.

Уффф. Устал. Надеюсь, длинная писанина хоть немного расставила все по местам.

Так что именно вам было нужно? Если просто добавить альфу - то .putalpha.
nauman
Андрей СветловСпасибо за целую лекцию. :) Общая картина немного вырисовывается. Если я правильно понял, tostring() вызванная без аргументов, пишет побитно в строку как есть, ничего не перекодируя(минимум нагрузки). fromstring() - raw mode нужно угадывать? Там довольно приличный список RGB. И что если размеры картинки не совпадут, их тоже нужно передавать отдельно?
В общей картине планируется сделать снимок и по сети отправить клиенту через сокет, поэтому и приходится преобразовывать в строку. Качество картинки большого значения не имеет.
Андрей Светлов
tostring без параметров будет писать в том, что получается из mode в raw mode по умолчанию.

Снимаете картинку.
Делаете tostring('raw', ‘RGB’) - альфа вам не нужна.
Кажется, ‘RGB’ у вас и так будет mode, между прочим.
Пересылаете - только не забудьте передать размеры.
20x20 или 10x40 - тот же объем данных, но результат будет несколько отличаться.
Потом выгребайте в том же ‘RGB’.

Всё. Для уменьшения объема передаваемых данных можно слать PNG или JPEG - но это слегка выходит за пределы обсуждения.
nauman
Андрей Светлов, я наверное чегото не допонимаю.
Вот синтаксис из оф. справки:
im.tostring(encoder, parameters) => string
Вы привели пример выше:
tostring('raw', ‘RGB’)
Исходя из примеров можно сделать вывод, что encoder - “raw”, parameters - “RGB” - это третья колонка в таблице из исходников.
Далее из оф. справки:
Image.fromstring(mode, size, data, decoder, parameters) => image
mode -RGB 1-ая колонка из таблици, с размером пока проблем нет, data - переменная со строкой, декодер - должен быть тот же что и инкодер - пишем “raw”, parameters - тот же, что и в функции tostring - “RGB” - 3-я колонка.
Подводя итог:
to_string = im.tostring('raw','RGB')
from_string = Image.fromstring(“RGB”, im_size, to_string, “raw”, “RGB”)
Получаем результат: “SystemError: unknown raw mode”
В случае если в функции tostring('raw') опустить второй аргумент ‘RGB’, то она запускается без сообщений об ошибке, но уже возникает ошибка в fromstring: “ValueError: not enough image data”
Где или что я сделал не правильно?
nauman
Пошел немного другим путем, тут схема: картинка -> строка, строка -> картинка работает:
import StringIO
from PIL import ImageGrab, Image
output = StringIO.StringIO()
im_size = ImageGrab.grab().size
im = ImageGrab.grab().quantize(colors=96, method=1)
im.save(output, "PNG")
content = output.getvalue()
ima=Image.open(StringIO.StringIO(content))
ima.save('C:\\Python26\\screen'+'.png','PNG')
Возвращаясь к старой модели:
im.mode
результат “P”
to_string = im.tostring('raw')
from_string = Image.fromstring("P", im_size, to_string, "raw", "P")
Интерпретировалось без ошибок, но изображение никакое. Надо наверное вначале конвертить картинку, потом преобразовывать в строку.
Андрей Светлов
Во первых, вы что-то напутали.
b = im.tostring('raw', ('RGB',))
im2 = Image.fromstring('RGB', im.size, b, ‘raw’, ('RGB',))
Т.е. все работает.

В поледнем случае изображение испортилось, думаю, потому что вы работаете в палитровом режиме, не передавая саму палитру.
Т.е. PIL какую-то сделает по умолчанию, но о том, чтобы она была той что нужно - не мечтайте.
nauman
Андрей Светлов, Спасибо.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB