Дитеринг
Что ж, у меня сегодня есть на это время, так что вот результат. Вы не предоставили свою цветовую палитру для плоттера, поэтому я извлек ее из полученных изображений, но вы можете использовать любую. Идея дизеринга проста: наше восприятие объединяет цвет в области, а не в отдельные пиксели, поэтому вам нужно использовать некоторый аккумулятор разницы в цвете того, что визуализируется и что должно быть отрисовано вместо этого, и добавлять это к следующему пикселю ...
Таким образом, области будут иметь примерно одинаковый цвет, но реально используется только дискретное количество цветов. Форма того, как обновлять эту информацию, может отличать дизеринг ветвления результата от многих методов. Все очень просто:
- сбросить цветовой аккумулятор до нуля
- process all pixels
- for each pixel add its color to accumulator
- найти наиболее близкое соответствие результата в вашей палитре
- визуализировать выбранный цвет палитры
- вычесть выбранный цвет палитры из аккумулятора
Вот ваше входное изображение (я их собрал):
![input](https://i.stack.imgur.com/LzM0Z.png)
Вот изображение результата для вашего источника:
![результат](https://i.stack.imgur.com/Hcfyl.png)
Цветные квадраты в верхнем левом углу - это просто палитра, которую я использовал (извлеченная из вашего изображения).
Вот код (C ++), который я делаю с помощью:
picture pic0,pic1,pic2;
// pic0 - source img
// pic1 - source pal
// pic2 - output img
int x,y,i,j,d,d0,e;
int r,g,b,r0,g0,b0;
color c;
List<color> pal;
// resize output to source image size clear with black
pic2=pic0; pic2.clear(0);
// create distinct colors pal[] list from palette image
for (y=0;y<pic1.ys;y++)
for (x=0;x<pic1.xs;x++)
{
c=pic1.p[y][x];
for (i=0;i<pal.num;i++) if (pal[i].dd==c.dd) { i=-1; break; }
if (i>=0) pal.add(c);
}
// dithering
r0=0; g0=0; b0=0; // no leftovers
for (y=0;y<pic0.ys;y++)
for (x=0;x<pic0.xs;x++)
{
// get source pixel color
c=pic0.p[y][x];
// add to leftovers
r0+=WORD(c.db[picture::_r]);
g0+=WORD(c.db[picture::_g]);
b0+=WORD(c.db[picture::_b]);
// find closest color from pal[]
for (i=0,j=-1;i<pal.num;i++)
{
c=pal[i];
r=WORD(c.db[picture::_r]);
g=WORD(c.db[picture::_g]);
b=WORD(c.db[picture::_b]);
e=(r-r0); e*=e; d =e;
e=(g-g0); e*=e; d+=e;
e=(b-b0); e*=e; d+=e;
if ((j<0)||(d0>d)) { d0=d; j=i; }
}
// get selected palette color
c=pal[j];
// sub from leftovers
r0-=WORD(c.db[picture::_r]);
g0-=WORD(c.db[picture::_g]);
b0-=WORD(c.db[picture::_b]);
// copy to destination image
pic2.p[y][x]=c;
}
// render found palette pal[] (visual check/debug)
x=0; y=0; r=16; g=pic2.xs/r; if (g>pal.num) g=pal.num;
for (y=0;y<r;y++)
for (i=0;i<g;i++)
for (c=pal[i],x=0;x<r;x++)
pic2.p[y][x+(i*r)]=c;
где picture
- это мой класс изображения, поэтому здесь несколько членов:
xs,ys
разрешение
color p[ys][xs]
прямой доступ к пикселям (32-битный формат пикселей, 8 бит на канал)
clear(DWORD c)
заливка изображения цветом c
color
- это просто union
из DWORD dd
и BYTE db[4]
для простого доступа к каналу.
List<>
- это мой шаблон (динамический массив / список>
List<int> a
совпадает с int a[]
.
add(b)
добавить к нему букву b в конце списка
num
- количество элементов в списке
Теперь, чтобы избежать слишком большого количества точек (для продления срока службы вашего плоттера), вы можете вместо этого использовать разные шаблоны линий и т. Д., Но для этого требуется много проб / ошибок ... Например, вы можете подсчитать, сколько раз цвет используется в какой-либо области и из этого соотношения используйте разные шаблоны заливки (на основе линий). Вам нужно выбирать между качеством изображения и скоростью рендеринга / долговечностью ...
Без дополнительной информации о возможностях вашего плоттера (скорости, способе смены инструмента, поведении сочетания цветов) трудно выбрать лучший метод формирования потока управления. Готов поспорить, вы меняете цвета вручную, чтобы отображать каждый цвет сразу. Итак, извлеките все пиксели с цветом первого инструмента, объедините соседние пиксели в линии / кривые и отрендерите ... затем перейдите к следующему цвету инструмента ...
person
Spektre
schedule
24.04.2016