Enhancing dynamic range and normalizing illumination
Дело в том, чтобы нормализовать фон для бесшовной цвета первого. Существует много способов сделать это. Вот что я пробовал для вашего изображения:
создать таблицу для бумаги/чернильных клеток для изображения (так же, как в связанном ответе). Таким образом, вы выбираете размер ячейки сетки, достаточно большой, чтобы отличать характерные функции от фона. Для вашего изображения я выбираю 8x8 пикселей. Разделите изображение на квадраты и вычислите цвет avg и абс цвет для каждого из них. Затем отметьте насыщенные (разность малых абс) и установите их как бумажные или чернильные ячейки в соответствии со средним цветом по сравнению со всем цветом изображения.
Теперь просто обработайте все линии изображения и для каждого пикселя просто получите левую и правую бумажные ячейки. и линейно интерполировать между этими значениями. Это должно привести к фактическому цвету фона этого пикселя, чтобы просто вычесть его из изображения.
Моя реализация C++ для этого выглядит следующим образом:
color picture::normalize(int sz,bool _recolor,bool _sbstract)
{
struct _cell { color col; int a[4],da,_paper; _cell(){}; _cell(_cell& x){ *this=x; }; ~_cell(){}; _cell* operator = (const _cell *x) { *this=*x; return this; }; /*_cell* operator = (const _cell &x) { ...copy... return this; };*/ };
int i,x,y,tx,ty,txs,tys,a0[4],a1[4],n,dmax;
int x0,x1,y0,y1,q[4][4][2],qx[4],qy[4];
color c;
_cell **tab;
// allocate grid table
txs=xs/sz; tys=ys/sz; n=sz*sz; c.dd=0;
if ((txs<2)||(tys<2)) return c;
tab=new _cell*[tys]; for (ty=0;ty<tys;ty++) tab[ty]=new _cell[txs];
// compute grid table
for (y0=0,y1=sz,ty=0;ty<tys;ty++,y0=y1,y1+=sz)
for (x0=0,x1=sz,tx=0;tx<txs;tx++,x0=x1,x1+=sz)
{
for (i=0;i<4;i++) a0[i]=0;
for (y=y0;y<y1;y++)
for (x=x0;x<x1;x++)
{
dec_color(a1,p[y][x],pf);
for (i=0;i<4;i++) a0[i]+=a1[i];
}
for (i=0;i<4;i++) tab[ty][tx].a[i]=a0[i]/n;
enc_color(tab[ty][tx].a,tab[ty][tx].col,pf);
tab[ty][tx].da=0;
for (i=0;i<4;i++) a0[i]=tab[ty][tx].a[i];
for (y=y0;y<y1;y++)
for (x=x0;x<x1;x++)
{
dec_color(a1,p[y][x],pf);
for (i=0;i<4;i++) tab[ty][tx].da+=abs(a1[i]-a0[i]);
}
tab[ty][tx].da/=n;
}
// compute max safe delta dmax = avg(delta)
for (dmax=0,ty=0;ty<tys;ty++)
for (tx=0;tx<txs;tx++)
dmax+=tab[ty][tx].da;
dmax/=(txs*tys);
// select paper cells and compute avg paper color
for (i=0;i<4;i++) a0[i]=0; x0=0;
for (ty=0;ty<tys;ty++)
for (tx=0;tx<txs;tx++)
if (tab[ty][tx].da<=dmax)
{
tab[ty][tx]._paper=1;
for (i=0;i<4;i++) a0[i]+=tab[ty][tx].a[i]; x0++;
}
else tab[ty][tx]._paper=0;
if (x0) for (i=0;i<4;i++) a0[i]/=x0;
enc_color(a0,c,pf);
// remove saturated ink cells from paper (small .da but wrong .a[])
for (ty=1;ty<tys-1;ty++)
for (tx=1;tx<txs-1;tx++)
if (tab[ty][tx]._paper==1)
if ((tab[ty][tx-1]._paper==0)
||(tab[ty][tx+1]._paper==0)
||(tab[ty-1][tx]._paper==0)
||(tab[ty+1][tx]._paper==0))
{
x=0; for (i=0;i<4;i++) x+=abs(tab[ty][tx].a[i]-a0[i]);
if (x>dmax) tab[ty][tx]._paper=2;
}
for (ty=0;ty<tys;ty++)
for (tx=0;tx<txs;tx++)
if (tab[ty][tx]._paper==2)
tab[ty][tx]._paper=0;
// piecewise linear interpolation H-lines
int ty0,ty1,tx0,tx1,d;
if (_sbstract) for (i=0;i<4;i++) a0[i]=0;
for (y=0;y<ys;y++)
{
ty=y/sz; if (ty>=tys) ty=tys-1;
// first paper cell
for (tx=0;(tx<txs)&&(!tab[ty][tx]._paper);tx++); tx1=tx;
if (tx>=txs) continue; // no paper cell found
for (;tx<txs;)
{
// fnext paper cell
for (tx++;(tx<txs)&&(!tab[ty][tx]._paper);tx++);
if (tx<txs)
{
tx0=tx1; x0=tx0*sz;
tx1=tx; x1=tx1*sz;
d=x1-x0;
}
else x1=xs;
// interpolate
for (x=x0;x<x1;x++)
{
dec_color(a1,p[y][x],pf);
for (i=0;i<4;i++) a1[i]-=tab[ty][tx0].a[i]+(((tab[ty][tx1].a[i]-tab[ty][tx0].a[i])*(x-x0))/d)-a0[i];
if (pf==_pf_s ) for (i=0;i<1;i++) clamp_s32(a1[i]);
if (pf==_pf_u ) for (i=0;i<1;i++) clamp_u32(a1[i]);
if (pf==_pf_ss ) for (i=0;i<2;i++) clamp_s16(a1[i]);
if (pf==_pf_uu ) for (i=0;i<2;i++) clamp_u16(a1[i]);
if (pf==_pf_rgba) for (i=0;i<4;i++) clamp_u8 (a1[i]);
enc_color(a1,p[y][x],pf);
}
}
}
// recolor paper cells with avg color (remove noise)
if (_recolor)
for (y0=0,y1=sz,ty=0;ty<tys;ty++,y0=y1,y1+=sz)
for (x0=0,x1=sz,tx=0;tx<txs;tx++,x0=x1,x1+=sz)
if (tab[ty][tx]._paper)
for (y=y0;y<y1;y++)
for (x=x0;x<x1;x++)
p[y][x]=c;
// free grid table
for (ty=0;ty<tys;ty++) delete[] tab[ty]; delete[] tab;
return c;
}
Смотрите связанный ответ для более подробной информации.Вот результат для входного изображения после переключения шкалы серого <0,765>
и использования pic1.normalize(8,false,true);
Binarize
Я пытался наивным простой диапазон tresholding первым, так что если все значения цвета канала (R, G , в) в диапазоне <min,max>
он перекрасили, чтобы c1
еще, чтобы c0
:
void picture::treshold_AND(int min,int max,int c0,int c1) // all channels tresholding: c1 <min,max>, c0 (-inf,min)+(max,+inf)
{
int x,y,i,a[4],e;
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
dec_color(a,p[y][x],pf);
for (e=1,i=0;i<3;i++) if ((a[i]<min)||(a[i]>max)){ e=0; break; }
if (e) for (i=0;i<4;i++) a[i]=c1;
else for (i=0;i<4;i++) a[i]=c0;
enc_color(a,p[y][x],pf);
}
}
после применения pic1.treshold_AND(0,127,765,0);
и преобразования обратно в RGBA я получил этот результат:
серый шум из-за сжатия JPEG (PNG будет слишком большим). Как вы можете видеть, результат более или менее приемлем.
Если этого недостаточно, вы можете разделить свое изображение на сегменты. Вычислите гистограмму для каждого сегмента (она должна быть бимодальной), затем найдите цвет между двумя максимумами, которые являются вашей суммой. Проблема заключается в том, что фон охватывает гораздо большую площадь, так что пик чернил является относительно небольшим, а иногда трудно определить в линейных шкалах см полной гистограмма изображения:
При этом для каждого сегмента это будет намного лучше (так как будет много меньше цветных/цветных цветных кровотечений вокруг трестов), поэтому разрыв будет более заметным. Также не забывайте игнорировать небольшие пробелы (отсутствующие вертикальные линии на гистограмме), поскольку они просто связаны с квантованием/кодированием/округлением (не все серые оттенки присутствуют на изображении), поэтому вы должны отфильтровывать промежутки, меньшие, чем несколько интенсивностей, заменяющих их с помощью последней и следующей допустимой записи гистограммы.
Я попробую это - не нашел способ добавить это в мой проект obj-C, но будет проводить исследования, чтобы увидеть, как я могу добавить метод к объекту изображения. – Xav
- это фотография только мата? – Xav
@Xav В случае, если вы используете 3-х стороннее изображение lib, не стоит с ним связываться. Вы можете написать пользовательскую функцию вне класса изображения, которая вместо этого преобразует такое изображение в качестве операнда. Да, мое изображение представляет собой 2D-массив/матрицу пикселей, где пиксель 32-битный без знака int ('DWORD') с поддерживаемыми кодировками' _pf_rgba (4x8bit uint), _pf_u (1x32bit uint), _pf_s (1x32bit int) и более '' dec_color/enc_color' просто распакуйте/упакуйте это в массив 'DWORD [4]' для каждого канала, чтобы сделать код универсальным для любого пиксельного формата. Вы можете игнорировать все это, поскольку у вас есть только оттенки серого. – Spektre