сохранение PImage в 1 битное (монохромное) BMP изображение, проблема сглаживания контуров.
- Войдите на сайт для отправки комментариев
Помогите решить проблему с выравниванием контуров в 1 битном монохромном BMP изображении. Делаю проект "Электронные ценники"(ЭЦ) на Ардуино (esp8266), XAMP (это такой веб-сервер, все в одном пакете: PHP, Apach, Mysql), processing. ЭЦ использует монохромный (без оттенков серого) дисплей на электронных чернилах. Мне нужно в Processing генерировать 1 битные BMP картинки, эту функцию я написал для Processing сам, т.к. последний сохраняет по умолчанию только 24 битные BMP картинки.
PImage img1; void setup() { size(500, 400); img1 = loadImage("/data/24_bits.bmp"); //img2 = loadImage("flower.jpg"); // //img1.filter(DILATE); //img1.filter(ERODE); //img1.filter(BLUR, 0.5); //img1.filter(THRESHOLD, 0.5); img1.filter(THRESHOLD, 0.7); sav_BMP_1bit("/data/1_bits.bmp", img1); } void draw() { image(img1, 0, 0); //image(img2, width*2, 0); } void sav_BMP_1bit(String sav_path, PImage img){ //описание формата BMP: http://www.dragonwins.com/domains/getteched/bmp/bmpfileformat.htm println("начинаю строить 1 битное изображение BMP..."); println("Заголовок файла..."); //====заголовок файла============================ byte[] buf = {0x42,0x4d};//BM int w_img = img.width; int h_img = img.height; print("Ширина изображения: "); println(w_img); print("Высота изображения: "); println(h_img); int zero_bytes = 0;//количество нулевых байт вконце строки, что бы количество байт одной строки было кратно 4 if( (w_img / 8) % 4 != 0 && (w_img % 8 == 0) ){//если не кратно 4 количество байт одной строки и кратно 8 for(int c = 0; c < 10; c++){ if( ((w_img / 8) + c) % 4 == 0){//найдено большее кратное 4 число zero_bytes = c; break; } } } if( (w_img / 8) % 4 != 0 && (w_img % 8 != 0) ){//если не кратно 4 количество байт одной строки и не кратно 8 for(int c = 0; c < 10; c++){ if( ((w_img / 8) + c) % 4 == 0){//найдено большее кратное 4 число zero_bytes = c-1; break; } } } print("zero_bytes: "); println(zero_bytes); //zero_bytes--; int biSizeImage = (w_img * h_img) / 8 + (zero_bytes * h_img); print("Размер изображения в байтах: "); println(biSizeImage); //append(test,byte2(0)); int bfSize = 14+40+ 8+((w_img * h_img)/8) + (zero_bytes * h_img);//? print("Размер файла в байтах: "); println(bfSize); byte [] size = new byte[4]; size[0] = byte(bfSize & 0xFF); size[1] = byte((bfSize >> 8) & 0xFF); size[2] = byte((bfSize >> 16) & 0xFF); size[3] = byte((bfSize >> 24) & 0xFF); buf = append(buf, byte(size[0]));//bfSize buf = append(buf, byte(size[1]));//bfSize buf = append(buf, byte(size[2]));//bfSize buf = append(buf, byte(size[3]));//bfSize buf = append(buf, byte(0x00));//bfReserved1 buf = append(buf, byte(0x00));//bfReserved1 buf = append(buf, byte(0x00));//bfReserved2 buf = append(buf, byte(0x00));//bfReserved2 buf = append(buf, byte(0x3e));//bfOffBits 62 байта buf = append(buf, byte(0x00));//bfOffBits buf = append(buf, byte(0x00));//bfOffBits buf = append(buf, byte(0x00));//bfOffBits println("Заголовок изображения..."); //================= заголовок изображения ============================ buf = append(buf, byte(0x28));//biSize 40byte buf = append(buf, byte(0x00));//biSize buf = append(buf, byte(0x00));//biSize buf = append(buf, byte(0x00));//biSize buf = append(buf, byte(w_img & 0xFF));//biWidth buf = append(buf, byte( (w_img >> 8) & 0xFF) );//biWidth buf = append(buf, byte( (w_img >> 16) & 0xFF) );//biWidth buf = append(buf, byte( (w_img >> 24) & 0xFF) );//biWidth buf = append(buf, byte(h_img & 0xFF) );//biHeight buf = append(buf, byte( (h_img >> 8) & 0xFF) );//biHeight buf = append(buf, byte( (h_img >> 16) & 0xFF) );//biHeight buf = append(buf, byte( (h_img>>24)& 0xFF) );//biHeight buf = append(buf, byte(0x01));//biPlanes buf = append(buf, byte(0x00));//biPlanes buf = append(buf, byte(0x01));//biBitCount buf = append(buf, byte(0x00));//biBitCount buf = append(buf, byte(0x00));//biCompression buf = append(buf, byte(0x00));//biCompression buf = append(buf, byte(0x00));//biCompression buf = append(buf, byte(0x00));//biCompression buf = append(buf, byte( biSizeImage & 0xFF) );//biSizeImage buf = append(buf, byte( (biSizeImage >> 8) & 0xFF));//biSizeImage buf = append(buf, byte((biSizeImage >> 16) & 0xFF));//biSizeImage buf = append(buf, byte((biSizeImage >> 24) & 0xFF));//biSizeImage buf = append(buf, byte(0x00));//biXPelsPerMeter buf = append(buf, byte(0x00));//biXPelsPerMeter buf = append(buf, byte(0x00));//biXPelsPerMeter buf = append(buf, byte(0x00));//biXPelsPerMeter buf = append(buf, byte(0x00));//biYPelsPerMeter buf = append(buf, byte(0x00));//biYPelsPerMeter buf = append(buf, byte(0x00));//biYPelsPerMeter buf = append(buf, byte(0x00));//biYPelsPerMeter buf = append(buf, byte(0x02));//biClrUsed buf = append(buf, byte(0x00));//biClrUsed buf = append(buf, byte(0x00));//biClrUsed buf = append(buf, byte(0x00));//biClrUsed buf = append(buf, byte(0x02));//biClrImportant buf = append(buf, byte(0x00));//biClrImportant buf = append(buf, byte(0x00));//biClrImportant buf = append(buf, byte(0x00));//biClrImportant println("Таблица цветов..."); //===================== Таблица цветов ================================= buf = append(buf, byte(0x00));//black buf = append(buf, byte(0x00));//black buf = append(buf, byte(0x00));//black buf = append(buf, byte(0x00));//black buf = append(buf, byte(0xFF));//white buf = append(buf, byte(0xFF));//white buf = append(buf, byte(0xFF));//white buf = append(buf, byte(0x00));//white println("Пиксельные данные..."); //=====================пиксельные данные================================ color c; int data = 0; int bits_offset = 7; int counts_bytes = 0; //PImage test_img = loadImage("G:/Programs/XAMPP/htdocs/public_html/img/red_nambers.bmp"); //int pixel = 0; //test_img.loadPixels(); for(int rows = h_img; rows > 0; rows--){//ряды пикселей for(int columns = 0; columns < w_img; columns++){//столбцы пикселей c = img.get(columns, rows); //test_img.pixels[pixel] = c; // c = img.pixels[pixel]; //pixel++; if((c >> 16 & 0xFF) < 180 && (c >> 8 & 0xFF) < 180 && (c & 0xFF) < 180){ //print("."); data |= (0 << bits_offset);//black pixel } /* else if(c == white){ //print(" "); data |= (1 << bits_offset);//white pixel } */ else if( (c >> 16 & 0xFF) > 180 && (c >> 8 & 0xFF) > 180 && (c & 0xFF) > 180) {//default color data |= (1 << bits_offset);//white pixel }else{ //print("Новый цвет: R:");print( (c>>16) & 0xFF); print(", G:");print( (c>>8) & 0xFF);print(", B:");println( c & 0xFF); //data |= (1 << bits_offset);//white pixel //data |= (0 << bits_offset);//black pixel } if(bits_offset == 0 || columns == (w_img - 1) ){ bits_offset = 7; buf = append(buf, byte(data)); counts_bytes++; //print(binary(byte(data) )); data = 0; }else if(bits_offset > 0){ bits_offset--; } } for(int z = 0; z < zero_bytes; z++){ buf = append(buf, byte(0x00));//дабовление нулевых байт для кратности 4 counts_bytes = counts_bytes + 1; } // println(); } print("counts_bytes: "); println(counts_bytes); //biSizeImage = counts_bytes; //buf[0x22] = byte( biSizeImage & 0xFF); //buf[0x23] = byte( (biSizeImage >> 8) & 0xFF); //buf[0x24] = byte( (biSizeImage >> 16) & 0xFF); //buf[0x25] = byte( (biSizeImage >> 24) & 0xFF); println("Сохраняю..."); saveBytes(sav_path ,buf); // //test_img.updatePixels(); //test_img.save(path_folder_images + "/" + "def2" + ".bmp"); print("1 битное изображение BMP сохранено в: "); println(sav_path); }
Проблема с текстом в картинках 1 бит, контуры становятся кривыми (особенно заметно на диагональных линиях, а особо тонкие в шрифтах и вовсе исчезают), т.к. оказалось что в 24 битных BMP для выравнивания контуров используются пикселя с оттенками серого (если текст черный) и при построении 1 битного BMP эти пикселя серые теряются (их конечно можно группировать в черный или белый пиксель, но все равно не то) . Кстати Paint точно так же сохраняет в 1 битное изображение. Исходник и пример BMP ЭЦ в папке "data" на Яндекс Диске:
А хочется-то чего?
Хочется что бы линии в шрифтах были ровнее, т.е. менее заметней "ступеньки". В общем что бы 1 битное BMP выглядело как 24 битное, монохромное. Какой то алгоритм нужен который будет дорисовывать черные пиксели к черному тексту. Да и вообще как то же делают шрифты C++ для Ардуино из привычных шрифтов ttf, otf и выглядят они ровно на экранах наших. Вообще ни когда не думал что шрифты ttf, otf, могут содержать оттенки серого для сглаживания по контуру символа.
Какой то алгоритм нужен который будет дорисовывать черные пиксели к черному тексту
И будет просто новая ступенька.
они станут мельче, толщиной хотя бы в 1 пиксель, а не 5 - 10 пикселей.
А зачем ты рендеришь шрифты с разрешением меньше разрешения экрана?
Нет, шрифт загружается из ttf или otf нужного размера с помощью функции:
Вот окно настроек BMP цеников: https://yadi.sk/i/QXx3weT1RIAcAQ
Если у тебя лесенки больше одного пикселя, то это неправильное разрешение.
Дело не в размере шрифта, проведите эксперемент в Paint, нарисуйте текст встроенными средствами Paint и сохраните картинку в 2-х вариантах: Монохромное 1 битное BMP изо и 24-х битное BMP. И посмотрите на разницу.
Конечно не в размере дело. Дело в разрешении. Если ты говоришь правду, и тебя ступеньки в 10 пикселей.
Что такое антиальясинг я прекрасно знаю. И субпиксельный хинтинг тоже. И что ни то ни то не может работать на однобитной глубине цвета.
Зато на однобитном экране может работать дизеринг.
Правда, я не уверен, что ТС нужно именно это (я вообще не понял, что ТС хочет. Разумеется, кроме абсурдного "что бы 1 битное BMP выглядело как 24 битное" - если бы такое было возможно, никто не стал бы делать 24-битное).
Да, видимо вы правы, функция : createFont(path_font, size);
Использует самое низкое разрешение шрифта, а аргумент size просто растягивает его до нужного размера.
Это ужасно, есть в Processing и другой способ использования шрифтов ttf, otf, конвертация установленных в систему шрифтов в другой формат файла, но что то мне подсказывает, что там будет все тоже самое.
https://processing.org/reference/loadFont_.html
http://forum.amperka.ru/threads/Векторная-графика-и-шрифт-на-Ардуино.16660/
Почему мой установленный в систему шрифт не доступен в меню: "Инструменты" -> "Создать шрифты..."?
https://yadi.sk/i/ARhgjUrMtZRiKA
Например шрифт "Crystal Normal" установился и доступен во всех программах Windows, но в processing он не виден, только с помощью функции "PFont.list()" он есть в списках.
Но при попытке его выбрать получаю следующую ошибку: https://yadi.sk/i/Pwfv3uCdiJi_nQ
С другими же шрифтами из списка "PFont.list()" все нормально, только с моими шрифтами такая проблема.