2016-08-30 10 views
2

Я пытаюсь подготовить изображения для распознавания текста, и до сих пор здесь, что я сделал, используя информацию из Extracting text OpenCVOpenCV для оптического распознавания символов: Как вычислить уровни пороговых значений для серого изображения OCR

Из полученного изображения я использую контуры, которые были отфильтрованы, чтобы сделать маску следующим образом:

//this is the mask of all the text 
Mat maskF = Mat::zeros(rgb.rows, rgb.cols, CV_8UC1); 
// CV_FILLED fills the connected components found - CV_FILLED to fill 
drawContours(maskF, letters, -1, Scalar(255), CV_FILLED); 
cv::imwrite("noise2-Mask.png", maskF); 

в результате IMG перспективно: enter image description here

считая это был мой первоначальный IMG: enter image description here

К сожалению, запущенный Tesseract на нем дает некоторые проблемы, я думаю, что уровни серых, которые вы видите между буквами на словах, смущают tesseract - так что вы думаете, да, давайте сделаем двоичное преобразование, хорошо, что просто пропускает вторую половину , поэтому я также попытался применить порог Otsu, но текст становится неровным, а персонажи теряют свою форму.

Я попытался CalcBlockMeanVariance от OpenCV Adaptive Threshold OCR, но не мог заставить его компиляцию (и я не уверен, что я понимаю, что это все ТБЙ) компилировать дроссели на

res=1.0-res; 
res=Img+res; 

Во всяком случае, если у кого есть какие-либо предложения, я буду признателен Это! Обратите внимание, что фракции редко признаются Tesseract, но я пишу новый учебный набор, который, надеюсь, улучшит скорость reco)

ответ

0

Вы можете попытаться стереть весь текст с эрозией, а затем вычесть результат из изображения в градациях серого или использовать преобразование tophat вместо того, чтобы унифицировать ваш фон для глобального порога! Here вы можете найти пример кода! И почему бы не использовать существующую функцию adaptiveThreshold?

2
  1. 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);

    normalize

  2. 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 я получил этот результат:

    binarise

    серый шум из-за сжатия JPEG (PNG будет слишком большим). Как вы можете видеть, результат более или менее приемлем.

    Если этого недостаточно, вы можете разделить свое изображение на сегменты. Вычислите гистограмму для каждого сегмента (она должна быть бимодальной), затем найдите цвет между двумя максимумами, которые являются вашей суммой. Проблема заключается в том, что фон охватывает гораздо большую площадь, так что пик чернил является относительно небольшим, а иногда трудно определить в линейных шкалах см полной гистограмма изображения:

    bimodal histogram

    При этом для каждого сегмента это будет намного лучше (так как будет много меньше цветных/цветных цветных кровотечений вокруг трестов), поэтому разрыв будет более заметным. Также не забывайте игнорировать небольшие пробелы (отсутствующие вертикальные линии на гистограмме), поскольку они просто связаны с квантованием/кодированием/округлением (не все серые оттенки присутствуют на изображении), поэтому вы должны отфильтровывать промежутки, меньшие, чем несколько интенсивностей, заменяющих их с помощью последней и следующей допустимой записи гистограммы.

+0

Я попробую это - не нашел способ добавить это в мой проект obj-C, но будет проводить исследования, чтобы увидеть, как я могу добавить метод к объекту изображения. – Xav

+0

- это фотография только мата? – Xav

+0

@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