Zsákutca

     Na jó reggelt (12:55 :P)... Nem most keltem fel, csak kellett valami megszólítás az elejére. Tegnap este egész jól elszórakoztam ezzel a keretes képkereséssel, bár konkrét kézzelfogható eredményem nem igazán volt. Ideoda rakott kicsi karikákat és annyi. Mindjárt dobok be pár képet, csak ez a grrrrrr ... Integral image... Rosszabb, mint a matlab... Pedig azt hittem, annál rosszabb nem is létezik... Egy egyszerű összeadási képleten dolgozom reggel 9 óta szinte semmi eredménnyel... (Az egyetlen eredmény az, hogy megértettem, hogyan is kellene működjön, ha jól működne...)

      Mindjárt begorombulok... (vajon van egyáltalán ilyen szó???)...

      13:30 - Megvan a hiba. Az első ciklusban nem kezeltem le a színcsatornákat. :D Ez nem jó hír, de legalább megvan a hiba.
      13:49 - A hiba ki van javítva. Ennek ellenére az integral image-t nem rajzolja ki. Valami nem szimpatikus a programnak az én integral imagemben...
      13:54 - Na ki a király? (Üvegtigris - Sanyi a király :P ) ... Rakok be ebből is pár képet... Egész ügyes. És működik... „Kicsit savanyú, kicsit sárga… de a mienk” (A Tanú című filmből idézve...)

     Megyek ebédelni, utána jöhet a képkivágás.



IntegralImage

















Forráskód

#include <highgui.h>


int main()

{

    IplImage* img;

    IplImage* intimage;

    IplImage* int_show;

    uchar *data;

    int *int_image_data;

    uchar *int_show_data;

    int i,j;

    int height,width,step,channels;

    int num, num_min_egy;

    int r1 = 0, r2 = 0, r3 = 0;


    img = cvLoadImage("d:\\Soundwave.jpg",1); //CV_LOAD_IMAGE_GRAYSCALE

    cvNamedWindow("PIC",CV_WINDOW_AUTOSIZE);

    cvNamedWindow("Integral",CV_WINDOW_AUTOSIZE);

    cvShowImage("PIC",img);


    data = (uchar *)img->imageData;


    height = img->height;

    width = img->width;

    step = img->widthStep;

    channels = img->nChannels;

    intimage = cvCreateImage( cvSize(width, height), IPL_DEPTH_32F, channels );

    int_show = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, channels );

    int_image_data = (int *) intimage->imageData;

    int_show_data = (uchar*)int_show->imageData;


    //--------------------------------------------------------------------


    //Integral Image szamolas


    int_image_data[0] = data[0];


    // Az elso sor

    for (i = 1; i < step; i++)

    {

      

        int_image_data[i] = int_image_data[i-1] + data[i];

        int_show_data[i]=int_image_data[i]/(width*height);

        //printf("%i -> %i\n",int_image_data[i],int_show_data[i]);

    }


    // A tobbi sor

    for(i=1; i<(int)(height) ; i++)

    {

        r1 = 0, r2 = 0, r3 = 0;


        for(j=0;j <(int)(width);j++)

            {

                //itt figyelni kell arra, hogy tobb szincsatorna van.

                num=i*step+j*channels;

                num_min_egy=(i-1)*step+j*channels;

                r1+=data[num];

              

                int_image_data[num]=int_image_data[num_min_egy]+r1;

                int_show_data[num]=int_image_data[num]/(width*height);


                num=i*step+j*channels+1;

                num_min_egy=(i-1)*step+j*channels+1;

                r2+=data[num];


                int_image_data[num]=int_image_data[num_min_egy]+r2;

                int_show_data[num]=int_image_data[num]/(width*height);


                num=i*step+j*channels+2;

                num_min_egy=(i-1)*step+j*channels+2;

                r3+=data[num];


                int_image_data[num]=int_image_data[num_min_egy]+r3;

                int_show_data[num]=int_image_data[num]/(width*height);

            }

    }

    // Integral Image szamolas vege.

    // az int_image_data tartalmazza a konkret integral ertekeket

    // az int_show_data pedig egy megjelenitheto valtozatat tartalmazza az

    // int_image_datanak (0 es 255 koze beallitva az ertekeket.)

    //----------------------------------------------------


    // mivel az int_image_data kirajzolasa sehogy nem sikerult, igy egy kis

    // "huncutsaghoz" folyamodtam, vagyis az adatokat belemasoltam az eredeti

    // kep informacioit tartalmazo vektorba, es azt jelenitettem meg. Ez magan a

    // szerkezeten semmit nem valtoztatott. Egyszeruen nekem csak igy mukodik...

    for(i=0; i<(int)(step*height) ; i++)

    {

        data[i] = int_show_data[i];

    }


    cvShowImage("Integral",img);

    //------------------------------------


    // takaritas

    cvWaitKey();

     cvDestroyWindow("PIC");

    cvDestroyWindow("Integral");

    cvReleaseImage(&intimage);

    cvReleaseImage(&img);

    cvReleaseImage(&int_show);


    return 0;

}

      Ha valakit komolyabban érdekel (hmmm... ebben erősen kételkedem), akkor szivesen kommentelem, hogy melyik rész mit csinál. A .cpp fájlt sajnos nem tudom ide feltölteni (a blog szerver csak képet enged feltölteni...) viszont tudok linkelni egy másik inspirációs anyagot, amit én is felhasználtam a program megírásában.

      Ez 32 bites lebegőpontos ábrázolású képnek számolja ki az integrálját szintén 32 bites képet térítve vissza.  http://www.etalonsoft.com/code/integral.cpp



Keretezés 

     A keretezésnek az a lényege, hogy egy előre jól meghatározott keret-rendszerrel végigpásztázom a képemet, bizonyos szabályszerűségek után kutatva. Amennyire én tudom, a "nagyok" is így csinálták meg az arcfelismerést az OpenCV-s könyvtári függvényekhez. Lényege, hogy egyszerű szabályrendszert állítunk fel, és ha mindenik szabálynak megfelel a képnek egy része, akkor elmondhatjuk, hogy az egy arc. 

     A könyvtári függvény esetében lehetőségünk van ezeknek a paramétereknek a hangolására (más szóval tanítani tudjuk a rendszert), hogy azt ismerjen fel, amit mi elvárunk tőle. Én konkrétan a szemöldök közötti részt céloztam meg. Ehhez egy T alakú keretet használtam. Ennek a keretnek az a lényege, hogy átlagot
számolunk minden egyes kis négyzet alatti pixelekből (mivel fekete-fehér képekkel dolgoztam, így ez csak egy színcsatornát jelent), és összehasonlítjuk az egyes kis négyzetek alatti átlagértékeket. A kis négyzetek 3oX3o pixel méretűek, persze könnyedén lehet skálázni őket. Az összehaonlítási kritériumok egyszerűek kell legyenek, hogy ne igényeljen nagy számolási időt. Nálam két összehasonlítási kritérium van: először keresek a felső három négyzet szerint, vagyis keresek olyan helyet, ahol egy világos területet két sötét terület vesz közre. Azt is megmondtam, hogy a sötét terület átlaga legalább 15 egységgel kell sötétebb legyen, mint a világos terület átlaga. Ha ez a feltétel teljesül, akkor megyünk a második szintre. Itt az alsó három négyzetnek az értékét figyelem és hasonlítom össze szintén a jobb és bal felső négyzetek átlagértékével. Itt küszöbérték nincs meghatározva. Csak legyen világosabb és boldog leszek.

      Felmerül az emberben a kérdés, hogy hogy lesz ebből arcfelismerés (elvégre édesanyámat sem ilyen módszerrel ismerem fel, meg a szomszéd Juliskát sem ;) ). Ez csak az első lépés az arcfelismerés fele. A "nagyoknál" (OpenCV haarcascade_frontface_alt.xml) 21 szint és átlagban 15o-2oo közötti szűrő van, majdhogynem tökéletesen behangolva. Ők aztán tényleg tudják, hogy hogyan kell arcot felismerni.

      Itt jön a témába az IntegralImage fogalom. Ahhoz, hogy én ki tudjam számítani egy régiónak az átlag pixelszámát, ahhoz végig kell járni a régiót és össze kell adni az összes pixelnek az értékét, ami abban a régióban található, majd el kell osztani a pixelek számával. Egyszerű matematika. Viszont, ha ezt egy 64oX48o pixel méretű képnél akarja az ember megcsinálni az elég időigényes feladat. Figyelembe véve azt, hogy az arc mérete nem mindig ugyanakkora egy képen belül, vagyis szem előtt kell tartani a szűrő skálázhatóságát, akkor már ki is futottunk a valósidejű arcfelismerés feladatköréből. Ekkor jött a Viola-Jones páros (akik nem ma kezdték) és elmagyarázták, hogyan működik az IntegralImage.

     Részletesebb leírást a http://www.cse.yorku.ca/~kosta/CompVis_Notes/integral_representations.pdf cikkben találhatunk.

Ennek lényege, hogy ha kiszámoljuk a leírásban megadott képlettel mindenik pixelhez tartozó integrált értéket, (vagyis a tőle balra és fölötte lévő értékeket összeadom), akkor az eredeti képen A-val jelölt területen lévő pixelek átlagértékét egy nagyon egyszerű képlettel ki tudom számítani, mégpedig : L4+L1-(L2+L3) eredményt elosztom a pixelszámmal. Ezzel minden egyes esetben, amikor dupla for ciklussal végig kellene pásztázzam a képet spóroltam egy nagy rakás processzoridőt.

      Ami a szép az egészben, hogy én ezt nem így oldottam meg. Persze az enyém még csak 1.0a verzió. Én a RegionOfInterest és cvMean függvényeket használtam. Az első kijelöli a képnek azt a részét, amivel éppen dolgozni akarok, a másik pedig kiszámolja az átlagát (mindenképp sokkal lassabban, mint a Viola-Jones páros képlete).



Forráskód

#include <highgui.h>

#include <cv.h>

#include <stdio.h>

#include "math.h"


int main()

{

    double avg1=0,avg2=0,avg3=0,avg4=0,avg5=0,avg6=0;

    int scan_rect_size=30;

    int x=0,y=0,width=scan_rect_size,height=scan_rect_size;

    int tsh1=15;

    IplImage* img;

    IplImage* temp;


    img=cvLoadImage("D:\\paris.jpg",CV_LOAD_IMAGE_GRAYSCALE);

            cvSmooth( img, img, CV_BLUR, 5,5 );

            temp = cvCloneImage( img );

            CvSize size = cvGetSize(img);

            //printf("Mikrofonpróba 1 2 3.\n");

            cvNamedWindow("Processed",CV_WINDOW_AUTOSIZE);

            cvShowImage("Processed",temp);

            for (y=0;y<(size.height-height);y=y+4)  

            {

                for (x=0;x<(size.width-(width*3));x=x+4)

                {

                    cvSetImageROI(img,cvRect(x,y,width,height));

                    avg1 = cvMean(img);

                    cvResetImageROI(img);

                    cvSetImageROI(img,cvRect(x+width,y,width,height));

                    avg2 = cvMean(img);

                    cvResetImageROI(img);

                    cvSetImageROI(img,cvRect(x+(2*width),y,width,height));

                    avg3 = cvMean(img);

                    cvResetImageROI(img);


                    if ((avg1<avg2)&&(avg3<avg2)&&(avg2-avg1>tsh1)&&(avg2-avg3>tsh1))

                    { //ez azt jelenti, hogy az elso feltetelen tuljutottunk

                         //cvCircle(temp,cvPoint(x+(width*1.5),y+(height/2)),5,cvScalar(250,0,0));

                         //cvWaitKey();

                        if ((x+width<size.width)&&(y+(3*height)<size.height))

                        {

                                    cvSetImageROI(img,cvRect(x+width,y+height,width,height));

                                    avg4 = cvMean(img);

                                    cvResetImageROI(img);

                                    cvSetImageROI(img,cvRect(x+width,y+(2*height),width,height));

                                    avg5 = cvMean(img);

                                    cvResetImageROI(img);

                                    cvSetImageROI(img,cvRect(x+width,y+(3*height),width,height));

                                    avg6 = cvMean(img);

                                    cvResetImageROI(img);

                                    if ((avg1<avg4)&&(avg1<avg5)&&(avg1<avg6)&&(avg3<avg4)&&(avg3<avg5)&&(avg3<avg6))

                                    {

                                        printf("Masodik szint:\t Avg1 : %f\tAvg2 : %f\tAvg3 : %f\n",avg1,avg2,avg3);

                                        cvCircle(temp,cvPoint(x+(width*1.5),y+(height/2)),5,cvScalar(250,0,0));

                                        cvShowImage("Processed",temp);

                                    }

                        }

                    }           

                }

            }

    cvNamedWindow("Processed",CV_WINDOW_AUTOSIZE);

    cvShowImage("Processed",temp);


    cvWaitKey();

    cvReleaseImage(&img);

    cvReleaseImage(&temp);

    cvDestroyWindow("Original");


    return 0;

}


Eredmények






       Ezek csak a jó eredmények. Rosszat nem rakok fel, mert minek? :P



      A képek egy előzetes áltagoló-simító szűrőn mentek keresztül, azért ilyen homályosak. Jól látható, hogy a program még nagyon messze van a tökéletestől. Keretméret és küszöbérték változtatásával ennél sokkal másabb eredményeket is el lehet érni.
A program további fejlesztési lehetősége egy harmadik feltétel lenne, mégpedig, hogy a T fölött keressen még két oldalt egy-egy fekete csíkot (a szemöldököt). Valami ilyenre gondoltam. Vagy mittudomén. Most fogok neki, s megpróbálom kideriteni, hogy konkrétan mit is használnak a nagyok... Utána meg lazulás meg spagetti :D
Holnap fogok neki rendszerelméletnek meg a PLC projektet debugolom, emiatt nem fogok pár napig ide semmit írni. Viszont lélekben mindíg itt leszek... Már alig várom, hogy lejárjon a szesszió.

0 megjegyzés:

Megjegyzés küldése

Return top