Input Image

inputImg

InputData

Optical Flow

  • Optical Flow란?
    영상 내 물체의 움직임 패턴

  • 가정

    1. 연속된 프레임 사이에서 움직이는 물체의 픽셀 intensity는 변함이 없다. (color constancy)
    2. 이웃하는 픽셀은 비슷한 움직임을 가진다.(Brightness Constraint)
  • Aperture Problem
    하나의 픽셀만 관찰할 경우 조건 부족으로 인해 실제 움직임을 잘못 판별하는 문제가 발생함.

  • Lucas & Kanade Method
    주변 픽셀은 유사한 움직임을 갖는다는 조건을 이용해 추가적인 equation을 구함으로써 해결함.

result

1to2

2to3

3to4

  • 범위를 1~4로 설정했을 때의 결과이다.
  • 정지해 있는 산은 변화가 적고 상대적으로 움직이거나 밝기 변화가 있는 폭포와 하늘은 변화가 큰 모습을 볼 수 있다.

Code

void MainFrame::on_button_opticalflow_clicked()
{
    int StartImgnum = ui->spinBox_StartImg->value();
    int EndImgnum = ui->spinBox_EndImg->value();

    std::vector<KImageGray> Imgvec;

    QString q_fileName;

    for(int i = StartImgnum; i <= EndImgnum; i++)
    {
        if(i<10)
        {
            q_fileName = QString::fromStdString("./data/yos.0" + std::to_string(i) + ".pgm");
        }
        else q_fileName = QString::fromStdString("./data/yos." + std::to_string(i) + ".pgm");

        ImageForm*  q_pForm = new ImageForm(q_fileName, "Open", this);
        _plpImageForm->Add(q_pForm);

        KImageGray* PGM = &q_pForm->ImageGray();//.GaussianSmoothed(2);
        Imgvec.emplace_back(*PGM);
    }

    qDebug()<<"1";

    int row = Imgvec[0].Row();
    int col = Imgvec[0].Col();

    UV** mat_uv = new UV*[row]{0,};

    for(int i = 0; i < row; i++)
    {
        mat_uv[i] = new UV[col]{ {0,0},};
    }


    //Optical_Flow(Imgvec[0],Imgvec[2],mat_uv);
    //Draw(Imgvec[0],mat_uv);


    for(int i = 0; i < Imgvec.size()-2; i++)
    {
        Optical_Flow(Imgvec[i],Imgvec[i+2],mat_uv);
        Draw(Imgvec[i],mat_uv);

        ImageForm*  q_pForm2 = new ImageForm(Imgvec[i],"Optical Flow", this);
        _plpImageForm->Add(q_pForm2);
        q_pForm2->show();
    }




}
typedef struct UV{
    double u;
    double v;
}UV;

'21-1학기 > 컴퓨터비전' 카테고리의 다른 글

7. SIFT  (0) 2021.08.19
6. Hough Transform  (0) 2021.08.19
5. Canny Edge Operator  (0) 2021.08.19
4. Gaussian noise and salt&pepper noise & Box, Gaussian, Median Filter  (0) 2021.08.19
3. Histogram Equalization & Histogram Matching  (0) 2021.08.18

Input Image

InputImg

SIFT

  • SIFT란?
    이미지의 크기와 회전에 불변하는 특징을 추출하는 알고리즘

Scale-Space

Scale-Space

sigma : 1

DOG

DOG

result

SIFT

SIFT 특징을 찾고 DB 생성

Code

void MainFrame::on_button_GSS_DOG_clicked()
{
    KImageGray icMain,Origin,Half1,Half2;



    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
    }

    else
        return;

    KImageColor icMain2(icMain.GrayToRGB()), icMain3(icMain.GrayToRGB());

    //Scale space 생성
    double sigma = ui->Spinsigma->value();

    KImageDouble igMain(icMain);
    Origin = igMain.ToGray();
    Half1 = igMain.HalfSize().ToGray();
    Half2 = igMain.HalfSize().HalfSize().ToGray();

    std::vector<std::vector<KImageDouble>> Octave(3); //Octave1,2,3


    for(int j=0;j<5;j++)
    {
        //qDebug() <<"1";
        Octave[0].push_back(Gaussian_Filter(Origin,sigma*pow(1.41,(j-1))));
        //qDebug() <<"2";
        Octave[1].push_back(Gaussian_Filter(Half1,sigma*pow(1.41,(j-1))));
        //qDebug() <<"3";
        Octave[2].push_back(Gaussian_Filter(Half2,sigma*pow(1.41,(j-1))));

    }



    //Dog
    std::vector<std::vector<KImageDouble>> Dog(3);
    for(int i = 0; i < 3;i++)
    {
        KImageDouble Dog_img(Octave[i][0].Row(),Octave[i][0].Col());

        for(int octave=1;octave<5;octave++)
        {

            for(int ii=0;ii<Octave[i][0].Row();ii++)
            {
                for(int jj=0;jj<Octave[i][0].Col();jj++)
                {
                    Dog_img[ii][jj] = Octave[i][octave][ii][jj]-Octave[i][octave-1][ii][jj];
                }
            }

            Dog[i].push_back(Dog_img);
            //qDebug() <<Octave[i][0].Row();
        }
    }



    //KeyPoint & filtering
    std::vector<std::vector<real_key>> KeyPoint = Find_Key_Point(Dog); //루트2 시그마, 2시그마

    qDebug() << KeyPoint[0].size();
    qDebug() << KeyPoint[1].size();

    int x, y;





    Orientation(KeyPoint,Octave[0],sigma); //orientation
    KeyPoint_descriptor(KeyPoint,Octave[0],sigma);


    for(int scale = 0; scale<KeyPoint.size(); scale++) //root 2 sigma, 2 sigma
    {
        for(int i=0;i<KeyPoint[scale].size();i++)
        {
            Mark_KeyPoint(icMain, KeyPoint[scale][i].magnitude, KeyPoint[scale][i].direction, KeyPoint[scale][i].x, KeyPoint[scale][i].y);
        }
    }

    std::ofstream writeFile("sift.txt");


    for(int i = 0; i<KeyPoint.size();i++)
    {
        for(int j=0;j<KeyPoint[i].size();j++)
        {
            //KeyPoint[i][j].x " " KeyPoint[i][j].y " " KeyPoint[i][j].direction " " KeyPoint[i][j].magnitude
            //"\n"
            writeFile << KeyPoint[i][j].x << " " << KeyPoint[i][j].y << " " << KeyPoint[i][j].direction << " " << KeyPoint[i][j].magnitude
                      << "\n";


            for(int f = 0;f<4;f++)
            {
                for(int r = 0; r<KeyPoint[i][j].feature[f].size();r++)
                //KeyPoint[i][j].feature[f][r] " "
                    writeFile << KeyPoint[i][j].feature[f][r] << " ";
                writeFile << "\n";

            }
            //"\n"
            writeFile << "\n";
        }
      }




    //show

    ImageForm*  q_pForm1 = new ImageForm(icMain, "KeyPoint", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();



    ImageForm*  q_pForm2 = new ImageForm(Show(Octave), "Scale-Space", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

    ImageForm*  q_pForm3 = new ImageForm(Show(Dog), "Dog", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();



}
KImageDouble Gaussian_Filter(KImageGray& icMain, double sigma)
{

    KImageDouble Return(icMain);

    int row = icMain.Row();
    int col = icMain.Col();

    int Gau_filter_size = 8*sigma + 1; //filter size

    int size = std::sqrt(Gau_filter_size);

    //qDebug()<<size;

    double mask;



    for(int ii = 0; ii<row;ii++)
    {
        for(int jj = 0; jj<col;jj++)
        {

            if(ii<size || ii>=row-size || jj<size || jj>=col-size)
            {
                Return[ii][jj] = 0;
                continue;
            }
            mask = 0;


            for(int i = -size; i<size+1; i++)
            {
                for(int j = -size; j<size+1;j++)
                {
                    mask += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*icMain[ii-i][jj-j];

                }
            }

            Return[ii][jj] = (1/(2*M_PI*sigma*sigma))*mask;
        }

    }

    //qDebug()<<"good";
    return Return;
}
KImageGray Show(std::vector<std::vector<KImageDouble>>& Octave){

    int octave_que_size = Octave.size();
    int merged_row = Octave.front().front().Row() * 3;
    int merged_col = Octave.front().front().Col() * Octave.front().size();

    if (merged_col >= 1920) {
        merged_col = 1920;
    }
    KImageGray Base(merged_row, merged_col);


    std::vector<KImageDouble> img_vec;
    KImageGray now;

    int sum_of_prev_row = 0;


    for(int octave = 0; octave<Octave.size();octave++)
    {

        img_vec = Octave[octave];

        int img_vec_size = img_vec.size(); //6
        int each_row = img_vec.front().Row(); //359
        int each_col = img_vec.front().Col(); //640
        int poor_column = 0;


        for(int img = 0; img<img_vec_size;img++)
        {

            //qDebug()<<"ok";
            now = img_vec[img].ToGray();

            for(int i=0;i<each_row;i++)
            {
                for(int j =0; j<each_col;j++)
                {
                    Base[i+sum_of_prev_row][j+(img-poor_column)*each_col] = now[i][j];
                }
            }

            // 다음에 이어 붙일 이미지가 존재하지만 모니터의 가로 길이가 부족할 때 한 칸 아래로 내림
            if (each_col - 1 + ((img - poor_column) + 1) * each_col > merged_col && img + 1 < img_vec_size)
            {
                  sum_of_prev_row += each_row;
                  poor_column = img + 1;
            }
            //qDebug()<<"end";

        }
        sum_of_prev_row += each_row;

    }


    return Base;
}
bool IsPeak(std::vector<KImageDouble>& Dog_Scale,int i,int ii, int jj)
{
    if(Dog_Scale[i][ii][jj]<0.03) return false;

    if(Dog_Scale[i][ii][jj]>0) //극대
    {
         if(Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii-1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii-1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii+1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii+1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii+1][jj+1]||


            Dog_Scale[i][ii][jj]<Dog_Scale[i][ii-1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii-1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i][ii][jj-1]||                                               Dog_Scale[i][ii][jj]<Dog_Scale[i][ii][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj+1]||

            Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii-1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii-1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii+1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii+1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii+1][jj+1]) return false;

     }
     else
     {
         if(Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii-1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii-1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii+1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii+1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii+1][jj+1]||


            Dog_Scale[i][ii][jj]>Dog_Scale[i][ii-1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii-1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i][ii][jj-1]||                                               Dog_Scale[i][ii][jj]>Dog_Scale[i][ii][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj+1]||

            Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii-1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii-1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii+1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii+1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii+1][jj+1]) return false;
      }

    return true;

}
std::vector<std::vector<real_key>> Find_Key_Point(std::vector<std::vector<KImageDouble>>& Dog)
{

    double thres = 1.5;
    double r = thres;
    double b = pow(r+1,2)/r;
    double Dxx,Dyy,Dxy;
    double TrH, DetH;
    double a;

    real_key key;
    std::vector<std::vector<real_key>> key_vec(2);

    //scale = 0;
    for(int dog = 0; dog<3; dog++){ //octave 별 dog 선택
        for(int i=1;i<4;i++) //scale 변화
        {
            for(int ii=1;ii<Dog[dog][i].Row()-1;ii++)
            {
                for(int jj=1;jj<Dog[dog][i].Col()-1;jj++)
                {
                    //qDebug() << dog <<" "<<ii<<jj;


                    if(IsPeak(Dog[dog],i,ii,jj)) //6개 이미지 넘겨줌
                    {


                        Dxx = Dog[dog][i][ii][jj+1]+Dog[dog][i][ii][jj-1]-2*Dog[dog][i][ii][jj];
                        Dyy = Dog[dog][i][ii+1][jj]+Dog[dog][i][ii-1][jj]-2*Dog[dog][i][ii][jj];
                        Dxy = ((Dog[dog][i][ii+1][jj+1]-Dog[dog][i][ii+1][jj-1]) - (Dog[dog][i][ii-1][jj+1] - Dog[dog][i][ii-1][jj-1]))/4.0;
                        TrH = Dxx + Dyy;
                        DetH = Dxx*Dyy - Dxy*Dxy;

                        a = TrH*TrH/DetH;

                        //qDebug() << Dxx<<" "<<Dyy<<" "<<" " <<Dxy<<" " << DetH;
                        if(DetH<=0) continue;
                        //if(a < b) KeyPoint[ii][jj] = 255;
                        if(a >= b) continue;

                        //KeyPoint
                        key.x = ii*pow(2,dog);
                        key.y = jj*pow(2,dog);
                        key.scale = i;


                        key_vec[i-1].push_back(key);

                    }

                }

            }

        }
    }

    return key_vec;
}
void Orientation(std::vector<std::vector<real_key>>& KeyPoint, std::vector<KImageDouble> Octave_0,double sigma)
{

    KImageDouble Scale_img;
    int x,y;
    double mag;
    double phase;
    double gau_kenel;

    double bucket[36] = {0,};

    int x_t,y_t;

    int index;

    double max = 0.;
    int dir;

    //int cnt = 1;

    int KeyPoint_scale_size = 0;

    for(int scale = 0; scale<KeyPoint.size(); scale++) //root 2 sigma, 2 sigma
    {
        Scale_img = Octave_0[scale+1]; //origin roo2, origin 2
        sigma = sigma*pow(1.4,scale+1);
        KeyPoint_scale_size = KeyPoint[scale].size();
        for(int i=0;i<KeyPoint_scale_size;i++)
        {
            x = KeyPoint[scale][i].x;
            y = KeyPoint[scale][i].y;

            //16개 좌표 x+w_x, y+w_y
            for(int w_x = -5; w_x < 6; w_x++)
            {
                for(int w_y = -5; w_y < 6; w_y++)
                {
                    x_t = x+w_x;
                    y_t = y+w_y;

                    if(x_t>Scale_img.Row()-2||x_t<1||y_t>Scale_img.Col()-2||y_t<1) continue;
                    gau_kenel = 1./(2*M_PI*sigma*sigma) * exp(-0.5*(w_x*w_x + w_y*w_y)/(sigma*sigma));

                    mag = gau_kenel * sqrt(pow(Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t],2)
                                       +pow(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],2));

                    phase = atan2(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],
                                  Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t])*180/M_PI+180.0;

                    //if(phase>360) phase -= 360;

                    //if(phase<0) phase += 180;

                    index = phase/10;

                    bucket[index] += mag;

                    //qDebug() << scale << " " << i << " " << x << " " << y << " " << x_t << " " << y_t << " " << index << mag;
                    //qDebug() << index;
                }
            }

            for(int b = 0; b<36; b++)
            {
                if(bucket[b]>max)
                {
                    max = bucket[b];
                    dir = b*10;
                }

                qDebug()<< b << " " << bucket[b];
            }

            //qDebug()<< dir << " " << max;

            for(int b = 0; b<36; b++)
            {
                if(bucket[b]>max*0.8&& b != dir)
                {
                    KeyPoint[scale].push_back({KeyPoint[scale][i].x,KeyPoint[scale][i].y,(double)b*10,bucket[b]});
                   //qDebug()<< b << " " << bucket[b];
                }

                //qDebug()<< b << " " << bucket[b];
            }


            KeyPoint[scale][i].direction = dir;
            KeyPoint[scale][i].magnitude = max;
            //qDebug()<< cnt << " " << dir << bucket[8] << bucket[9];
            //cnt++;


        }

    }


}
void KeyPoint_descriptor(std::vector<std::vector<real_key>>& KeyPoint, std::vector<KImageDouble> Octave_0,double sigma)
{

    KImageDouble Scale_img;
    int x,y;
    double mag[81];
    double phase[81];
    double gau_kenel;

    double bucket[8] = {0,};

    int x_t,y_t;




    //int cnt = 1;

    int KeyPoint_scale_size = 0;

    for(int scale = 0; scale<KeyPoint.size(); scale++) //root 2 sigma, 2 sigma
    {
        Scale_img = Octave_0[scale+1]; //origin roo2, origin 2
        sigma = sigma*pow(1.4,scale+1);
        KeyPoint_scale_size = KeyPoint[scale].size();
        for(int i=0;i<KeyPoint_scale_size;i++)
        {


            x = KeyPoint[scale][i].x;
            y = KeyPoint[scale][i].y;

            //11개 좌표 x+w_x, y+w_y
            for(int w_x = -4; w_x < 5; w_x++)
            {
                for(int w_y = -4; w_y < 5; w_y++)
                {
                    x_t = x+w_x;
                    y_t = y+w_y;

                    if(x_t>Scale_img.Row()-2||x_t<1||y_t>Scale_img.Col()-2||y_t<1) continue;
                    gau_kenel = 1./(2*M_PI*sigma*sigma) * exp(-0.5*(w_x*w_x + w_y*w_y)/(sigma*sigma));

                    mag[(w_x+4)*8+w_y+4] = gau_kenel * sqrt(pow(Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t],2)
                                                            +pow(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],2));

                    phase[(w_x+4)*8+w_y+4] = atan2(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],
                                                    Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t])*180/M_PI+180.0;

                }
            }

            //index = phase/45
            for(int p = 0; p < 2; p++)
            {
                std::vector<double> dou_vec;

                bucket[(int)phase[0+5*p]/45] += mag[0+5*p];
                bucket[(int)phase[1+5*p]/45] += mag[1+5*p];
                bucket[(int)phase[2+5*p]/45] += mag[2+5*p];
                bucket[(int)phase[3+5*p]/45] += mag[3+5*p];

                bucket[(int)phase[9+5*p]/45] += mag[9+5*p];
                bucket[(int)phase[10+5*p]/45] += mag[10+5*p];
                bucket[(int)phase[11+5*p]/45] += mag[11+5*p];
                bucket[(int)phase[12+5*p]/45] += mag[12+5*p];

                bucket[(int)phase[18+5*p]/45] += mag[18+5*p];
                bucket[(int)phase[19+5*p]/45] += mag[19+5*p];
                bucket[(int)phase[20+5*p]/45] += mag[20+5*p];
                bucket[(int)phase[21+5*p]/45] += mag[21+5*p];

                bucket[(int)phase[27+5*p]/45] += mag[27+5*p];
                bucket[(int)phase[28+5*p]/45] += mag[28+5*p];
                bucket[(int)phase[29+5*p]/45] += mag[29+5*p];
                bucket[(int)phase[30+5*p]/45] += mag[30+5*p];


                for(int b = 0;b<8;b++)
                {
                    dou_vec.push_back(bucket[b]);
                }

                KeyPoint[scale][i].feature.push_back(dou_vec);
            }


            for(int p = 0; p < 2; p++)
            {
                std::vector<double> dou_vec;

                bucket[(int)phase[45+5*p]/45] += mag[45+5*p];
                bucket[(int)phase[46+5*p]/45] += mag[46+5*p];
                bucket[(int)phase[47+5*p]/45] += mag[47+5*p];
                bucket[(int)phase[48+5*p]/45] += mag[48+5*p];

                bucket[(int)phase[54+5*p]/45] += mag[54+5*p];
                bucket[(int)phase[55+5*p]/45] += mag[55+5*p];
                bucket[(int)phase[56+5*p]/45] += mag[56+5*p];
                bucket[(int)phase[57+5*p]/45] += mag[57+5*p];

                bucket[(int)phase[63+5*p]/45] += mag[63+5*p];
                bucket[(int)phase[64+5*p]/45] += mag[64+5*p];
                bucket[(int)phase[65+5*p]/45] += mag[65+5*p];
                bucket[(int)phase[66+5*p]/45] += mag[66+5*p];

                bucket[(int)phase[72+5*p]/45] += mag[72+5*p];
                bucket[(int)phase[73+5*p]/45] += mag[73+5*p];
                bucket[(int)phase[74+5*p]/45] += mag[74+5*p];
                bucket[(int)phase[75+5*p]/45] += mag[75+5*p];


                for(int b = 0;b<8;b++)
                {
                    dou_vec.push_back(bucket[b]);
                }

                KeyPoint[scale][i].feature.push_back(dou_vec);
            }


        }

    }


}
void Mark_KeyPoint(KImageGray &igScale, double mag, double ori_deg, int pX, int pY){
    double dX_circle , dY_circle;
    int iX_circle, iY_circle;

    double radius = 0;

    int iX_dir, iY_dir;
    double dX_dir, dY_dir;

    double custom = 4;

    int tmp = (int)(mag / custom);
    if(tmp < 2 ){
        radius = 5;
    }
    else if(tmp < 5){
        radius = 7;
    }
    else if(tmp < 10){
        radius = 8;
    }
    else{
        radius = 10;
    }

    double theta_rad;
    //Draw circle
    for(int angle = 0; angle <= 360; angle+= 1){

        theta_rad = (double)angle * 0.01745329; // 0.017453..=>1 / 180 * 3.14592; degree to rad

        dX_circle = (double)pX - (double)(radius * cos(theta_rad));
        dY_circle = (double)pY - (double)(radius * sin(theta_rad));

        iX_circle = (int)dX_circle; iY_circle = (int)dY_circle;

        if(iX_circle > 0 && iY_circle > 0 && iX_circle < (int)igScale.Row() && iY_circle < (int)igScale.Col()){
           igScale[iX_circle][iY_circle] = 255;
        }
    }

    //Draw Direction
    for(int range = 0; range <= radius; range++){
        theta_rad = ori_deg * 0.01745329; // 0.017453..=>1 / 180 * 3.14592; degree to rad

        dX_dir = (double)pX - (double)(range * cos(theta_rad));
        dY_dir = (double)pY - (double)(range * sin(theta_rad));

        iX_dir = (int)dX_dir; iY_dir = (int)dY_dir;

        if(iX_dir > 0 && iY_dir > 0 && iX_dir < (int)igScale.Row() && iY_dir < (int)igScale.Col()){
           igScale[iX_dir][iY_dir] = 255;
        }

    }

}
typedef struct real{
    int x,y;
    double scale;
    double direction = -1;
    double magnitude = -1;
    std::vector<std::vector<double>> feature;
}real_key;

Input Image

InputImg

  • Circle Hough Transform : 반지름이 주어져 있음
  • General Hough Transform : 도형의 모양이 좌표로 주어져 있음

Hough Transform

  • Hough Trnasform이란?
    기본적인 아이디어는 voting이다.
    1. 우선 찾으려는 도형의 파라미터를 변수로 정한다. ( 파라미터를 잘못 정할 경우 옳지 않은 경우가 생길 수 있으므로 모든 경우를 나타낼 수 있는 파라미터를 선정해야 한다.)
    2. 원래의 이미지의 한 점에서 구할 수 있는 모든 경우의 수를 파라미터 공간에 voting한다.
    3. voting이 가장 많은 파라미터가 우리가 구하려는 도형의 파라미터 변수로 정해진다.

Circle Hough Transform

  1. circle edge를 찾는 것이기 때문에 edge 점들에서만 voting을 해야한다. 이를 위해 canny edge operator 함수를 이용해 edge를 구한다.
  2. 반지름이 103 pixel로 주어져 있으므로 파라미터는 원의 중심점(a,b)이다.
  3. edge 점을 지나고 103pixel을 가진 원의 중심을 파라미터 공간에 voting한다.
  4. voting이 가장 많은 점을 원의 중심으로 해서 103 pixel의 반지름을 가진 원을 표시한다.

result

CircleImg

Code

void MainFrame::on_button_CircleHough_clicked()
{
    KImageGray icMain;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
    }
    else
        return;

    KImageGray icMain2(icMain.Row(),icMain.Col());

    //canny edge operator
    double sigma = ui->SpinBox_sigma->value();

    Canny_Edge(icMain,sigma);

    //Hough Transform
    int** voting = new int*[icMain.Row()]{0,};
    for(int i = 0; i<icMain.Row(); i++)
    {
        voting[i] = new int[icMain.Col()]{0,};
    }





    std::vector<Location> edge;
    Location location;



        //Edge 좌표 받아오기
    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){

            if(icMain[ii][jj] == 255)
            {
                location.x = ii;
                location.y = jj;
                edge.push_back(location);

                 //qDebug() << "x : " << ii << "y : " << jj;
            }

        }
    }


        //voting
    int x,y;
    int a,b;
    for(int i=0;i<edge.size();i++)
    {
        x = edge[i].x;
        y = edge[i].y;

        //qDebug() << "x : " << x << "y : " << y;

        for(int j=0;j<360;j++){
            a = x-51.5*cos(j*M_PI/180);
            b = y-51.5*sin(j*M_PI/180);


            if(a>0 && a<icMain.Row() && b>0 && b<icMain.Col())
            {
                voting[a][b]++;
                if(icMain2[a][b]+30 < 256)
                    icMain2[a][b] += 30;
                else icMain2[a][b]  = 255;
            }


        }


    }



        //max voting -> 원의 중심점 찾기
    int max = 0;

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            if(voting[ii][jj]>max){
                a = ii;
                b = jj;
                max = voting[ii][jj];
            }
        }
    }

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            icMain[ii][jj] = 0;
            //icMain2[ii][jj] = 0;
        }
    }


        //circle
    for(int j=0;j<360;j++)
    {
        x = a-51.5*cos(j*M_PI/180);
        y = b-51.5*sin(j*M_PI/180);
        icMain[x][y] = 255;
    }





    //Show
    ImageForm*  q_pForm1 = new ImageForm(icMain, "Circle Hough Transform", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

    ImageForm*  q_pForm2 = new ImageForm(icMain2, "Circle Hough Transform_voting", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();


}

General Hough Transform

  • 크기와 회전 각도가 고정일 때
    • 파라미터 3개
    • voting 공간 : X_c, Y_c
    1. 도형의 중심점을 잡고 파라미터 테이블을 만든다. 이 때 Edge Direction을 key로 사용한다.
    2. 아래의 수식을 이용해 각 edge에서 table를 돌면서 나올 수 있는 X_c와 Y_c의 경우를 파라미터 공간에 voting한다.

  • 크기와 회전 각도가 바뀔 때
    • 파라미터 5개
    • voting 공간 : X_c, Y_c, s, theta
    1. 크기와 회전 각도가 고정일 때와 같은 파라미터 테이블을 만든다.
    2. 위의 그림과 같이 voting 한다.

result

GeneralImg

Code

void MainFrame::on_button_GeneralHough_clicked()
{
    KImageGray icMain;
    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
    }
    else
        return;

    KImageGray icMain2(icMain.Row(),icMain.Col());

    int f_row[86] = {0,};
    int f_col[86] = {0,};

    //파일 읽기
    std::ifstream readFile;
    readFile.open("C:/Qt/plug.txt");
    int fx,fy,i=0;

    while(!readFile.eof()){
        readFile>>fx>>fy;
        f_col[i] = fy;
        f_row[i] = fx;
        i++;
    }




    //table
    int Xc=0, Yc=0; //중심점
    for(int i=0; i<86;i++)
    {
        Xc += f_row[i];
        Yc += f_col[i];
    }
    Xc /= 86;
    Yc /= 86;


    typedef struct r{
        double Ri,Ai;
    }R;

    std::vector<R> table[4];
    R t;
    int Xi,Yi;

        //Grad 구하기
    double dTmp, dGradX, dGradY;
    int dir;

    for(int j = 1,jj = 86-2; jj; j++, jj--)
    {
        dGradX = (float)(f_row[j+1] - f_row[j-1]) + 1e-8;
        dGradY = (float)(f_col[j+1] - f_col[j-1]) + 1e-8;

        dTmp = (180.0/M_PI)*(atan2(dGradY,dGradX)) + 90;
        dir = ((((int)(dTmp/22.5) + 1) >>1) & 0x00000003);

        //qDebug() << f_row[j] << " " << f_col[j] << " " <<dir;

        t.Ri = sqrt((Xc - f_row[j])*(Xc - f_row[j]) + (Yc - f_col[j])*(Yc - f_col[j]));
        t.Ai = (180.0/M_PI) * atan2((double)(Yc - f_col[j]),(double)(Xc - f_row[j]));

        table[dir].push_back(t);

    }


    //canny edge
    double sigma = ui->SpinBox_sigma->value();

    Canny_Edge(icMain,sigma);

    std::vector<Location> edge;
    Location location;

    for(int i=0;i<icMain.Row();i++)
    {
        for(int j=0;j<icMain.Col();j++)
        {
            if(icMain[i][j] == 255)
            {
                location.x = i;
                location.y = j;
                edge.push_back(location);
                //qDebug() << "x : " << i << "y : " << j;
            }
        }
    }




    //Hough Transform
    int** voting = new int*[icMain.Row()]{0,};
    for(int i = 0; i<icMain.Row(); i++)
    {
        voting[i] = new int[icMain.Col()]{0,};
    }


    /*
    for(int i=0; i < 4; i++)
    {
        qDebug() << i << " " << table[i].size();
    }
    //qDebug() << edge.size();
    */

        //voting
    for(int i = 0; i < edge.size(); i++)
    {
        Xi = edge[i].x;
        Yi = edge[i].y;

            //qDebug() << "ok";
        for(int j=0;j<4;j++)
        {

           for(int k=0;k<table[j].size();k++)
           {

                Xc = Xi - 0.85*table[j][k].Ri*cos(table[j][k].Ai*M_PI/180);
                Yc = Yi - 0.85*table[j][k].Ri*sin(table[j][k].Ai*M_PI/180);

                //qDebug() << "i : " << i << "j : " << j << "k : " << k << "x : " << Xc << "y : " << Yc;


                if(Xc>0 && Xc<icMain.Row() && Yc>0 && Yc<icMain.Col())
                {
                    voting[Xc][Yc]++;
                    if(icMain2[Xc][Yc]+50 < 256)
                        icMain2[Xc][Yc] += 50;
                    else icMain2[Xc][Yc]  = 255;
                }

           }

        }

    }



        //max voting -> 원의 중심점 찾기
    int max = 0;

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            if(voting[ii][jj]>max){
                Xc = ii;
                Yc = jj;
                max = voting[ii][jj];
            }
        }
    }

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            icMain[ii][jj] = 0;
        }
    }


        //find
    for(int j = 0; j < 4; j++)
    {
        for(int k = 0; k<table[j].size();k++)
        {
            Xi = Xc + 0.85*table[j][k].Ri*cos(table[j][k].Ai*M_PI/180);
            Yi = Yc + 0.85*table[j][k].Ri*sin(table[j][k].Ai*M_PI/180);

            if(Xi>0 && Xi<icMain.Row() && Yi>0 && Yi<icMain.Col())
            {
                icMain[Xi][Yi] = 255;
            }
        }
    }





    //Show
    ImageForm*  q_pForm1 = new ImageForm(icMain, "General Hough Transform", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

    ImageForm*  q_pForm2 = new ImageForm(icMain2, "General Hough Transform_voting", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

}






함수
void Canny_Edge(KImageGray& imgSrc,double sigma)
{
    KImageGray icMain = imgSrc;


    int row = icMain.Row();
    int col = icMain.Col();

    //Gaussian Filter

    int Gau_filter_size = 8*sigma + 1; //filter size

    int size = std::sqrt(Gau_filter_size);

    //qDebug()<<size;

    double mask;


    for(int ii = size; ii<row-size;ii++){
        for(int jj = size; jj<col-size;jj++){

            mask = 0;


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    mask += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*icMain[ii-i][jj-j];

                }
            }

            icMain[ii][jj] = (1/(2*M_PI*sigma*sigma))*mask;
        }
    }

    //Sobel Mask

    int dLow = 80;
    int dHigh = 120;
    double mag_x;
    double mag_y;
    double phase;

    double** magnitude = new double*[row]{0,};
    int** direction = new int*[row] {0,};
    for(int i = 0; i < row; i++)
    {
        magnitude[i] = new double[col]{0,};  // magnitude
        direction[i] = new int[col]{0,};
    }

    double Mask_w[3][3] = { {-1., 0., 1.}, {-2., 0., 2.}, {-1., 0. ,1.} };
    double Mask_h[3][3] = { {1., 2., 1.}, {0., 0., 0.}, {-1., -2. ,-1.} };

    //qDebug()<<row<< " "<<col;


    //qDebug()<<(unsigned char)((((int)(113/22.5)+1)>>1) & 0x00000003);

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){

            mag_x = 0;
            mag_y = 0;

            for(int i = -1; i<2; i++){
                for(int j = -1; j<2;j++){
                    mag_x += (Mask_w[i+1][j+1]*icMain[ii+i][jj+j]);
                    mag_y += (Mask_h[i+1][j+1]*icMain[ii+i][jj+j]);
                }
            }

            magnitude[ii][jj] = fabs(mag_x) + fabs(mag_y);

            if(magnitude[ii][jj] > dLow)
            {
                //icMain2[ii][jj] = 255;
                phase = atan2(mag_x,mag_y)*180/M_PI;
                if(phase>360) phase -= 360;

                if(phase<0) phase += 180;

                direction[ii][jj] = (unsigned char)((((int)(phase/22.5)+1)>>1) & 0x00000003);


                //qDebug()<<ii<<" "<<jj<<" "<<phase<<" "<<direction[ii][jj];


            }
            else {
                magnitude[ii][jj] = 0;
                //icMain2[ii][jj] = 0;
            }

        }
    }


    // Non-Maxima Suppression
    //int nDx[4] = {0, 1, 1, 1};
    //int nDy[4] = {1, 1, 0, -1};
    int nDx[4] = {0, 1, 1, -1};
    int nDy[4] = {1, 1, 0, 1};

    double** buffer = new double*[row] {0,};
    int** realedge = new int*[row]{0,};
    for(int i = 0; i < row; i++)
    {
        buffer[i] = new double[col]{0,};
        realedge[i] = new int[col]{0,};
    }

    class HighEdge{
        public:
            int x;
            int y;
    };

    HighEdge h;
    std::stack<HighEdge> highedge;

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            if(magnitude[ii][jj] == 0) continue;

            //if(magnitude[ii][jj] > magnitude[ii+nDx[direction[ii][jj]]][jj+nDy[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDx[direction[ii][jj]]][jj-nDy[direction[ii][jj]]])
            if(magnitude[ii][jj] > magnitude[ii+nDy[direction[ii][jj]]][jj+nDx[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDy[direction[ii][jj]]][jj-nDx[direction[ii][jj]]])
            {
                if(magnitude[ii][jj] > dHigh)
                {
                    h.x = ii;
                    h.y = jj;

                    highedge.push(h);
                    realedge[ii][jj] = 255;

                }

                //realedge[ii][jj] = 255;
                buffer[ii][jj] = magnitude[ii][jj];
            }
        }
    }


    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            //icMain2[ii][jj] = realedge[ii][jj];
        }
    }


    //Thresholding
    int x,y;
    while(!highedge.empty())
    {
        x = highedge.top().x;
        y = highedge.top().y;
        for(int i = -1; i < 2; i++)
        {
            for(int j = -1; j < 2; j++)
            {
                if(buffer[x+i][y+j] && buffer[x+i][y+j]<=dHigh)
                {
                    h.x = x+i;
                    h.y = y+j;
                    highedge.push(h);
                    realedge[x+i][y+j] = 255;
                    buffer[x+i][y+j] = 0;
                }
            }
        }
        highedge.pop();
    }

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            imgSrc[ii][jj] = realedge[ii][jj];
        }
    }

    delete[] direction;
    delete[] magnitude;
}

'21-1학기 > 컴퓨터비전' 카테고리의 다른 글

8. Optical Flow  (0) 2021.08.19
7. SIFT  (0) 2021.08.19
5. Canny Edge Operator  (0) 2021.08.19
4. Gaussian noise and salt&pepper noise & Box, Gaussian, Median Filter  (0) 2021.08.19
3. Histogram Equalization & Histogram Matching  (0) 2021.08.18

Canny Edge Operator

Input Image

InputImg

Canny Edge Operator

  1. 노이즈 제거를 위한 smoothing
  2. Sobel mask를 이용한 edge 후보 찾기
  3. Non-Maxima Suppression
  4. Hysteresis Thresholding
    1. Sobel mask를 이용한 edge 후보 찾기
      • magnitude를 이용해 edge의 direction을 구한다.
    1. Non-Maxima Suppression
      • Local Maxima를 선택해서 얇은 edge를 구할 수 있게 하는 과정
      • 2에서 구한 direction을 통해서 edge 후보의 양옆과 비교를 해서 최대인 경우 남김
    1. Hysteresis Thresholding
      • edge의 끊김 방지를 위해 Low Threshold를 넘고 주변에 edge가 있다면 edge로

result

resultImg

Code

void MainFrame::on_button_CannyEdge_clicked()
{

    KImageGray icMain;
    KImageGray icMain2;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
        icMain2 = _q_pFormFocused->ImageGray();
    }
    else
        return;

    int row = icMain.Row();
    int col = icMain.Col();

    //Gaussian smoothing
    double sigma = ui->SpinBox_sigma->value();

    int Gau_filter_size = 8*sigma + 1; //filter size

    int size = std::sqrt(Gau_filter_size);

    //qDebug()<<size;

    double mask;


    for(int ii = size; ii<row-size;ii++){
        for(int jj = size; jj<col-size;jj++){

            mask = 0;


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    mask += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*icMain[ii-i][jj-j];

                }
            }

            icMain[ii][jj] = (1/(2*M_PI*sigma*sigma))*mask;
        }
    }

    //Sobel Mask

    int dLow = 80;
    int dHigh = 120;
    double mag_x;
    double mag_y;
    double phase;

    double** magnitude = new double*[row]{0,};
    int** direction = new int*[row] {0,};
    for(int i = 0; i < row; i++)
    {
        magnitude[i] = new double[col]{0,};  // magnitude
        direction[i] = new int[col]{0,};
    }

    double Mask_w[3][3] = { {-1., 0., 1.}, {-2., 0., 2.}, {-1., 0. ,1.} };
    double Mask_h[3][3] = { {1., 2., 1.}, {0., 0., 0.}, {-1., -2. ,-1.} };

    //qDebug()<<row<< " "<<col;


    qDebug()<<(unsigned char)((((int)(113/22.5)+1)>>1) & 0x00000003);

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){

            mag_x = 0;
            mag_y = 0;

            for(int i = -1; i<2; i++){
                for(int j = -1; j<2;j++){
                    mag_x += (Mask_w[i+1][j+1]*icMain[ii+i][jj+j]);
                    mag_y += (Mask_h[i+1][j+1]*icMain[ii+i][jj+j]);
                }
            }

            magnitude[ii][jj] = fabs(mag_x) + fabs(mag_y);

            if(magnitude[ii][jj] > dLow)
            {
                //icMain2[ii][jj] = 255;
                phase = atan2(mag_x,mag_y)*180/M_PI;
                if(phase>360) phase -= 360;

                if(phase<0) phase += 180;

                direction[ii][jj] = (unsigned char)((((int)(phase/22.5)+1)>>1) & 0x00000003);


                qDebug()<<ii<<" "<<jj<<" "<<phase<<" "<<direction[ii][jj];


            }
            else {
                magnitude[ii][jj] = 0;
                //icMain2[ii][jj] = 0;
            }

        }
    }


    // Non-Maxima Suppression
    //int nDx[4] = {0, 1, 1, 1};
    //int nDy[4] = {1, 1, 0, -1};
    int nDx[4] = {0, 1, 1, -1};
    int nDy[4] = {1, 1, 0, 1};

    double** buffer = new double*[row] {0,};
    int** realedge = new int*[row]{0,};
    for(int i = 0; i < row; i++)
    {
        buffer[i] = new double[col]{0,};
        realedge[i] = new int[col]{0,};
    }

    class HighEdge{
        public:
            int x;
            int y;
    };

    HighEdge h;
    std::stack<HighEdge> highedge;

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            if(magnitude[ii][jj] == 0) continue;

            //if(magnitude[ii][jj] > magnitude[ii+nDx[direction[ii][jj]]][jj+nDy[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDx[direction[ii][jj]]][jj-nDy[direction[ii][jj]]])
            if(magnitude[ii][jj] > magnitude[ii+nDy[direction[ii][jj]]][jj+nDx[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDy[direction[ii][jj]]][jj-nDx[direction[ii][jj]]])
            {
                if(magnitude[ii][jj] > dHigh)
                {
                    h.x = ii;
                    h.y = jj;

                    highedge.push(h);
                    realedge[ii][jj] = 255;

                }

                //realedge[ii][jj] = 255;
                buffer[ii][jj] = magnitude[ii][jj];
            }
        }
    }


    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            //icMain2[ii][jj] = realedge[ii][jj];
        }
    }


    //Thresholding
    int x,y;
    while(!highedge.empty())
    {
        x = highedge.top().x;
        y = highedge.top().y;
        for(int i = -1; i < 2; i++)
        {
            for(int j = -1; j < 2; j++)
            {
                if(buffer[x+i][y+j] && buffer[x+i][y+j]<=dHigh)
                {
                    h.x = x+i;
                    h.y = y+j;
                    highedge.push(h);
                    realedge[x+i][y+j] = 255;
                    buffer[x+i][y+j] = 0;
                }
            }
        }
        highedge.pop();
    }

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            icMain2[ii][jj] = realedge[ii][jj];
        }
    }

    //Show
    ImageForm*  q_pForm1 = new ImageForm(icMain2, "Canny Edge Operator", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

}

Noise

Input Image

InputImg

Gaussian Noise

  • Gaussian Noise란?
    • 정규분포 Noise, 각 픽셀과 관련없이 독립적으로 생김.
    • 아래 수식에서 n은 정규분포를 가지는 잡음

Salt&Pepper Noise

  • Salt&Pepper Noise란?
    • impulsive noise or spike noise
    • 0 또는 1이 랜덤하게 생김

result

noise

Code

//포커스 된 ImageForm으로부터 영상을 가져옴
    KImageColor Gaussian_Noise;
    KImageColor Salt_Noise;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageColor().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        Gaussian_Noise = _q_pFormFocused->ImageColor();
        Salt_Noise = _q_pFormFocused->ImageColor();
    }
    else
        return;




    //Gaussian Noise 생성
    double sigma = ui->spin_GaussianParameter->value();

    KGaussian GauN;
    GauN.Create(0, sigma*sigma);
    GauN.OnRandom(Gaussian_Noise.Size());

    double dNoise = 0;
    double Kp = 8;
    for(int i = 0; i<Gaussian_Noise.Row();i++){
        for(int j = 0; j<Gaussian_Noise.Col();j++){
            dNoise = GauN.Generate();

            if(Gaussian_Noise[i][j].r + dNoise*Kp > 255)
                Gaussian_Noise[i][j].r = 255;
            else if(Gaussian_Noise[i][j].r +dNoise*Kp < 0)
                Gaussian_Noise[i][j].r = 0;
            else
                Gaussian_Noise[i][j].r += dNoise*Kp;

            if(Gaussian_Noise[i][j].g + dNoise*Kp > 255)
                Gaussian_Noise[i][j].g = 255;
            else if(Gaussian_Noise[i][j].g +dNoise*Kp < 0)
                Gaussian_Noise[i][j].g = 0;
            else
                Gaussian_Noise[i][j].g += dNoise*Kp;

            if(Gaussian_Noise[i][j].b + dNoise*Kp > 255)
                Gaussian_Noise[i][j].b = 255;
            else if(Gaussian_Noise[i][j].b +dNoise*Kp < 0)
                Gaussian_Noise[i][j].b = 0;
            else
                Gaussian_Noise[i][j].b += dNoise*Kp;
        }
    }


    //Salt Noise 생성
    srand((int)time(NULL));


    double Thres = 0.005;
    double minus_Thres = (double)(1-Thres);
    double random;


    for(int i=0; i<Salt_Noise.Row(); i++){
        for(int j=0;j<Salt_Noise.Col();j++){

            random = (double) rand()/RAND_MAX;

            if(random<Thres)
            {
                Salt_Noise[i][j].r = 0;
                Salt_Noise[i][j].g = 0;
                Salt_Noise[i][j].b = 0;
            }
            else if(random>minus_Thres){
                Salt_Noise[i][j].r = 255;
                Salt_Noise[i][j].g = 255;
                Salt_Noise[i][j].b = 255;
            }


        }
    }



    //show noise
    ImageForm*  q_pForm1 = new ImageForm(Salt_Noise, "Salt_and_Pepper Noise", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

    ImageForm*  q_pForm2 = new ImageForm(Gaussian_Noise, "Gaussian Noise", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

Box, Gaussian, Median Filter

Input Image

noise

Box Filter

  • Box Filtering
    • 주변 픽셀값의 평균을 얻는 방식의 이미지 필터링
    • 이미지 샘플과 filter kernel을 곱해서 필터링된 결과값을 얻음

  • 장점
    • 빠르다
    • Box(i+1) = Box(i) - neighborhood(i)[first] + neighborhood(i+1)[last]
  • 단점
    • Signal frequencies shared with noise are lose
    • Impulse noise is diffused but not removed
    • The secondary lobes let noise into the filtered image

sinc

result

BoxFilter

Code

    //필터링
    int filtersize = ui->spin_FilterSize->value();

    //Box Filtering
        //S_n_P noise 이미지 불러오기
    ImageForm* q_pForm_s = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Salt_and_Pepper Noise")
            {
                q_pForm_s = (*_plpImageForm)[i];
                break;
            }

    KImageColor Salt_Noise_2 = q_pForm_s->ImageColor();

    //Gaussian noise 이미지 불러오기

    ImageForm* q_pForm_g = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Gaussian Noise")
            {
                q_pForm_g = (*_plpImageForm)[i];
                break;
            }
    KImageColor Gaussian_Noise_2 = q_pForm_g->ImageColor();

    int size = 0.5*filtersize-0.5;
    int box = filtersize*filtersize;
    //qDebug()<<size;
        //Salt Noise
    double sum_r_s,sum_g_s,sum_b_s;
    double sum_r_g,sum_g_g,sum_b_g;

    for(int ii = size; ii<Salt_Noise_2.Row()-size;ii++){
        for(int jj = size; jj<Salt_Noise_2.Col()-size;jj++){
            sum_r_s = 0;
            sum_g_s = 0;
            sum_b_s = 0;

            sum_r_g = 0;
            sum_g_g = 0;
            sum_b_g = 0;
            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    sum_r_s+=Salt_Noise[ii-i][jj-j].r;
                    sum_g_s+=Salt_Noise[ii-i][jj-j].g;
                    sum_b_s+=Salt_Noise[ii-i][jj-j].b;

                    sum_r_g+=Gaussian_Noise[ii-i][jj-j].r;
                    sum_g_g+=Gaussian_Noise[ii-i][jj-j].g;
                    sum_b_g+=Gaussian_Noise[ii-i][jj-j].b;
                }
            }

            //qDebug()<<sum_r/(filtersize*filtersize);

            Salt_Noise_2[ii][jj].r=sum_r_s/box;
            Salt_Noise_2[ii][jj].g=sum_g_s/box;
            Salt_Noise_2[ii][jj].b=sum_b_s/box;

            Gaussian_Noise_2[ii][jj].r=sum_r_g/box;
            Gaussian_Noise_2[ii][jj].g=sum_g_g/box;
            Gaussian_Noise_2[ii][jj].b=sum_b_g/box;
        }
    }


    //show noise 제거
    ImageForm*  q_pForm3 = new ImageForm(Salt_Noise_2, "Box_Salt_n_Pepper", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();

    ImageForm*  q_pForm4 = new ImageForm(Gaussian_Noise_2, "Box_Gaussian", this);
    _plpImageForm->Add(q_pForm4);
    q_pForm4->show();

Gaussian Filter

  • 장점
    • Secondary lobes가 없으므로 정확한 low-pass filter 구현이 가능하다.

result

GaussianFilter

Code

    //Gaussian 필터링

    //S_n_P noise 이미지 불러오기
    ImageForm* q_pForm_s = 0;

    for(int i=0; i<_plpImageForm->Count();i++)
        if((*_plpImageForm)[i]->ID()=="Salt_and_Pepper Noise")
        {
            q_pForm_s = (*_plpImageForm)[i];
            break;
        }

    KImageColor Salt_Noise_2 = q_pForm_s->ImageColor();

    //Gaussian noise 이미지 불러오기

    ImageForm* q_pForm_g = 0;

    for(int i=0; i<_plpImageForm->Count();i++)
        if((*_plpImageForm)[i]->ID()=="Gaussian Noise")
        {
            q_pForm_g = (*_plpImageForm)[i];
            break;
        }
    KImageColor Gaussian_Noise_2 = q_pForm_g->ImageColor();

    int Gau_filter_size = 8*sigma + 1; //filter size




    int size = std::sqrt(Gau_filter_size);


    //qDebug()<<size;
        //Salt Noise

    double mask_r_s, mask_g_s, mask_b_s;
    double mask_r_g, mask_g_g, mask_b_g;


    for(int ii = size; ii<Salt_Noise_2.Row()-size;ii++){
        for(int jj = size; jj<Salt_Noise_2.Col()-size;jj++){

            mask_r_s = 0;
            mask_g_s = 0;
            mask_b_s = 0;

            mask_r_g = 0;
            mask_g_g = 0;
            mask_b_g = 0;


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    mask_r_s += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Salt_Noise[ii-i][jj-j].r;
                    mask_g_s += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Salt_Noise[ii-i][jj-j].g;
                    mask_b_s += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Salt_Noise[ii-i][jj-j].b;

                    mask_r_g += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Gaussian_Noise[ii-i][jj-j].r;
                    mask_g_g += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Gaussian_Noise[ii-i][jj-j].g;
                    mask_b_g += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Gaussian_Noise[ii-i][jj-j].b;
                }
            }

            Salt_Noise_2[ii][jj].r = (1/(2*M_PI*sigma*sigma))*mask_r_s;
            Salt_Noise_2[ii][jj].g = (1/(2*M_PI*sigma*sigma))*mask_g_s;
            Salt_Noise_2[ii][jj].b = (1/(2*M_PI*sigma*sigma))*mask_b_s;

            Gaussian_Noise_2[ii][jj].r = (1/(2*M_PI*sigma*sigma))*mask_r_g;
            Gaussian_Noise_2[ii][jj].g = (1/(2*M_PI*sigma*sigma))*mask_g_g;
            Gaussian_Noise_2[ii][jj].b = (1/(2*M_PI*sigma*sigma))*mask_b_g;



        }
    }


    //show noise 제거
    ImageForm*  q_pForm3 = new ImageForm(Salt_Noise_2, "Gaussian_Salt_n_Pepper", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();

    ImageForm*  q_pForm4 = new ImageForm(Gaussian_Noise_2, "Gaussian_Gaussian", this);
    _plpImageForm->Add(q_pForm4);
    q_pForm4->show();

Median Filter

  • Median Filtering
    • 주변 픽셀값을 sort한 후 중간값을 구하는 방식의 이미지 필터링
  • 장점
    • 경계가 흐려지지 않음
    • 중간값을 선택하는 방법이기 때문에 Salt and Pepper noise에 강함

  • 단점
    • sort 해야하므로 느리다

result

MedianFilter

Code

    //필터링
    int filtersize = ui->spin_FilterSize->value();

    //Median Filtering
        //S_n_P noise 이미지 불러오기
    ImageForm* q_pForm_s = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Salt_and_Pepper Noise")
            {
                q_pForm_s = (*_plpImageForm)[i];
                break;
            }

    KImageColor Salt_Noise_2 = q_pForm_s->ImageColor();

    //Gaussian noise 이미지 불러오기

    ImageForm* q_pForm_g = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Gaussian Noise")
            {
                q_pForm_g = (*_plpImageForm)[i];
                break;
            }
    KImageColor Gaussian_Noise_2 = q_pForm_g->ImageColor();


    int size = 0.5*filtersize-0.5;
    std::vector<double> v_r_s,v_g_s,v_b_s;
    std::vector<double> v_r_g,v_g_g,v_b_g;
    int box = filtersize*filtersize;

    //qDebug()<<size;
        //Salt Noise


    for(int ii = size; ii<Salt_Noise_2.Row()-size;ii++){
        for(int jj = size; jj<Salt_Noise_2.Col()-size;jj++){

            v_r_s.clear();
            v_g_s.clear();
            v_b_s.clear();

            v_r_g.clear();
            v_g_g.clear();
            v_b_g.clear();


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    v_r_s.push_back(Salt_Noise[ii-i][jj-j].r);
                    v_g_s.push_back(Salt_Noise[ii-i][jj-j].g);
                    v_b_s.push_back(Salt_Noise[ii-i][jj-j].b);

                    v_r_g.push_back(Gaussian_Noise[ii-i][jj-j].r);
                    v_g_g.push_back(Gaussian_Noise[ii-i][jj-j].g);
                    v_b_g.push_back(Gaussian_Noise[ii-i][jj-j].b);
                }
            }

            //qDebug()<<sum_r/(filtersize*filtersize);
            sort(v_r_s.begin(),v_r_s.end());
            sort(v_g_s.begin(),v_g_s.end());
            sort(v_b_s.begin(),v_b_s.end());

            sort(v_r_g.begin(),v_r_g.end());
            sort(v_g_g.begin(),v_g_g.end());
            sort(v_b_g.begin(),v_b_g.end());

            Salt_Noise_2[ii][jj].r = v_r_s[box/2];
            Salt_Noise_2[ii][jj].g = v_g_s[box/2];
            Salt_Noise_2[ii][jj].b = v_b_s[box/2];

            Gaussian_Noise_2[ii][jj].r = v_r_g[box/2];
            Gaussian_Noise_2[ii][jj].g = v_g_g[box/2];
            Gaussian_Noise_2[ii][jj].b = v_b_g[box/2];


        }
    }


    //show noise 제거
    ImageForm*  q_pForm3 = new ImageForm(Salt_Noise_2, "Median_Salt_n_Pepper", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();

    ImageForm*  q_pForm4 = new ImageForm(Gaussian_Noise_2, "Median_Gaussian", this);
    _plpImageForm->Add(q_pForm4);
    q_pForm4->show();

'21-1학기 > 컴퓨터비전' 카테고리의 다른 글

6. Hough Transform  (0) 2021.08.19
5. Canny Edge Operator  (0) 2021.08.19
3. Histogram Equalization & Histogram Matching  (0) 2021.08.18
2. Otsu’s Thresholding and Labeling & dilation and erosion  (0) 2021.08.18
1. Sepia Tone  (0) 2021.08.18

Histogram Equalization

Input Image

inputImg

Histogram Equalization

  • Histogram Equalization이란?
    이미지의 밝기를 조정해 contrast를 개선한다.
    contrast 높다 = 분산이 크다 = 분산이 가장 크려면 밝기 분포가 일정해야 한다 = 선명하다
  • idea

r과 s matching


result

resultHEQ

Code

void MainFrame::on_buttonHEQ_clicked()
{
    KImageColor icMain;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageColor().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageColor();
    }
    else
        return;


    //To get its histogramming
    int histo_R[256] = {0, };
    int histo_G[256] = {0, };
    int histo_B[256] = {0, };

    double P_R[256] = {0, };
    double P_G[256] = {0, };
    double P_B[256] = {0, };

    double T_R[256] = {0, };
    double T_G[256] = {0, };
    double T_B[256] = {0, };

    double r_R[256] = {0, };
    double r_G[256] = {0, };
    double r_B[256] = {0, };


    //histogram
    for(unsigned int i=0; i<icMain.Row(); i++){
        for(unsigned int j=0; j<icMain.Col(); j++)
        {
            histo_R[icMain[i][j].r] += 1;
            histo_G[icMain[i][j].g] += 1;
            histo_B[icMain[i][j].b] += 1;

        }
    }

    for(unsigned int t=0; t<256; t++){

        P_R[t] = (double)histo_R[t]/(double)icMain.Size();
        P_G[t] = (double)histo_G[t]/(double)icMain.Size();
        P_B[t] = (double)histo_B[t]/(double)icMain.Size();

        //qDebug() << t << " : " << P_R[t];

    }
    //


    // 대응
    T_R[0] = P_R[0];
    T_G[0] = P_G[0];
    T_B[0] = P_B[0];

    // 1/255 * r = T_R[r]

    for(unsigned int r=1; r<256; r++){
        T_R[r] = T_R[r-1] + P_R[r];
        r_R[r] = T_R[r] * 255;
        T_B[r] = T_G[r-1] + P_G[r];
        r_G[r] = T_G[r] * 255;
        T_G[r] = T_B[r-1] + P_B[r];
        r_B[r] = T_B[r] * 255;

        //qDebug() << r << " : " << T_R[r];

    }


    // 대입
    for(unsigned int i=0; i<icMain.Row(); i++){
        for(unsigned int j=0; j<icMain.Col(); j++)
        {
            icMain[i][j].r = r_R[icMain[i][j].r];
            icMain[i][j].g = r_R[icMain[i][j].g];
            icMain[i][j].b = r_R[icMain[i][j].b];
        }
    }

    ImageForm*  q_pForm2 = new ImageForm(icMain, "HEQ", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();
}

Histogram Matching

Input Image

imgInput

Histogram Matching

  • Histogram Matching이란?
    특정한 histogram과 Image의 histogram을 matching 시키는 것

  • idea

idea

result

resultHMA

Code

void MainFrame::on_buttonHMA_clicked()
{
    //포커스 된 ImageForm으로부터 영상을 가져옴
    KImageColor Source;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageColor().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        Source = _q_pFormFocused->ImageColor();
    }
    else
        return;


    ImageForm* q_pForm = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="target")
            {
                q_pForm = (*_plpImageForm)[i];
                break;
            }

    KImageColor Target=q_pForm->ImageColor();



    //get histogram
    int histo_S_R[256] = {0, };
    int histo_S_G[256] = {0, };
    int histo_S_B[256] = {0, };

    int histo_T_R[256] = {0, };
    int histo_T_G[256] = {0, };
    int histo_T_B[256] = {0, };

    for(unsigned int i=0; i<Source.Row(); i++){
        for(unsigned int j=0; j<Source.Col(); j++)
        {
            histo_S_R[Source[i][j].r] += 1;
            histo_S_G[Source[i][j].g] += 1;
            histo_S_B[Source[i][j].b] += 1;
        }
    }

    for(unsigned int i=0; i<Target.Row(); i++){
        for(unsigned int j=0; j<Target.Col(); j++)
        {
            histo_T_R[Target[i][j].r] += 1;
            histo_T_G[Target[i][j].g] += 1;
            histo_T_B[Target[i][j].b] += 1;
        }
    }


    //get P
    double P_S_R[256] = {0, };
    double P_S_G[256] = {0, };
    double P_S_B[256] = {0, };

    double P_T_R[256] = {0, };
    double P_T_G[256] = {0, };
    double P_T_B[256] = {0, };


    for(unsigned int t=0; t<256; t++){

        P_S_R[t] = (double)histo_S_R[t]/(double)Source.Size();
        P_S_G[t] = (double)histo_S_G[t]/(double)Source.Size();
        P_S_B[t] = (double)histo_S_B[t]/(double)Source.Size();

        P_T_R[t] = (double)histo_T_R[t]/(double)Target.Size();
        P_T_G[t] = (double)histo_T_G[t]/(double)Target.Size();
        P_T_B[t] = (double)histo_T_B[t]/(double)Target.Size();

        //qDebug() << t << " : " << P_S_R[t];

    }


    //get y , yp
    double y_R[256] = {0, };
    double y_G[256] = {0, };
    double y_B[256] = {0, };

    double yp_R[256] = {0, };
    double yp_G[256] = {0, };
    double yp_B[256] = {0, };

    y_R[0] = P_S_R[0];
    y_G[0] = P_S_G[0];
    y_B[0] = P_S_B[0];

    yp_R[0] = P_T_R[0];
    yp_G[0] = P_T_G[0];
    yp_B[0] = P_T_B[0];

    for(unsigned int r=1; r<256; r++){
        y_R[r] = y_R[r-1] + P_S_R[r];
        //r_R[r] = T_R[r] * 255;
        y_G[r] = y_G[r-1] + P_S_G[r];
        //r_G[r] = T_G[r] * 255;
        y_B[r] = y_B[r-1] + P_S_B[r];
        //r_B[r] = T_B[r] * 255;

        yp_R[r] = yp_R[r-1] + P_T_R[r];
        //r_R[r] = T_R[r] * 255;
        yp_G[r] = yp_G[r-1] + P_T_G[r];
        //r_G[r] = T_G[r] * 255;
        yp_B[r] = yp_B[r-1] + P_T_B[r];
        //r_B[r] = T_B[r] * 255;

        //qDebug() << r << " : " << yp_R[r];

    }


    int tr_R[256] = {0, };
    int tr_G[256] = {0, };
    int tr_B[256] = {0, };

    for(unsigned int i=0; i<256; i++){
         double min_R = 100000.0, min_G = 100000.0, min_B = 100000.0;
        for(unsigned int j = 0; j<256; j++)
        {
            if(min_R>std::fabs(y_R[i]-yp_R[j]))
            {
                min_R = std::fabs(y_R[i]-yp_R[j]);
                tr_R[i] = j;
            }

            if(min_G>std::fabs(y_G[i]-yp_G[j]))
            {
                min_G = std::fabs(y_G[i]-yp_G[j]);
                tr_G[i] = j;
            }

            if(min_B>std::fabs(y_B[i]-yp_B[j]))
            {
                min_B = std::fabs(y_B[i]-yp_B[j]);
                tr_B[i] = j;
            }
        }
        //qDebug() << i << " : " << min_R;
    }


    for(unsigned int i=0; i<256; i++)
    {
        qDebug() << i << " : " << tr_R[i];
    }


    for(unsigned int i=0; i<Source.Row(); i++){
        for(unsigned int j=0; j<Source.Col(); j++)
        {
            Source[i][j].r=tr_R[Source[i][j].r];
            Source[i][j].g=tr_G[Source[i][j].g];
            Source[i][j].b=tr_B[Source[i][j].b];
        }
    }



    ImageForm*  q_pForm2 = new ImageForm(Source, "Histogram Matching", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

    //ImageForm*  q_pForm1 = new ImageForm(Target, "target", this);
    //_plpImageForm->Add(q_pForm1);
    //q_pForm1->show();

}

Input Image

Input

Otsu’s Thresholding and Labeling

Thresholding

- Optimal Thresholding
    - A criterion functions should be devised that yields some measure of separation between regions.
    - The intensity value maximizing(or minimizing) the criterion function is considered as the optimal threshold.

Otsu's Thresholding

Otsu's

result

Otsu's_Thresholding

Code

KImageGray igImg;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        igImg = _q_pFormFocused->ImageGray();

    }
    else
        return;

    //To get its histogramming
    int histo[256] = {0, };
    double sigmab[256] = {0,};
    double N = igImg.Size() ,p,q1,q2,u,u1=0,u2=0,temp_q1,T;
    double max = -1;


    //histogram
    for(unsigned int i=0; i<igImg.Row(); i++){
        for(unsigned int j=0; j<igImg.Col(); j++)
        {
            histo[igImg[i][j]] += 1;
        }
    }


    for(int t = 0; t<255;t++){
       p = (double)histo[t] / (double) N;
       u += ((double)t)*p;
    }

    //Find T
    p = (double)histo[0]/(double)N;

    q1 = p;
    q2 = 1.0 - q1;
    sigmab[0]=q1*q2*(u1-u2)*(u1-u2);


    for(int t=1;t<256;t++)
    {
        p = (double)histo[t]/(double)N;
        temp_q1 = q1;
        q1 += p;
        q2 = 1.0 - q1;
        if(q1 ==0)
        {
            u1=0;
            u2 = (u - q1 * u1) / (1.0 - q1);
        }
        else if(q2 == 0)
        {
            u2=0;
            u1 = ((temp_q1 * u1) + ((double)(t)*p)) / (q1);
        }
        else
        {
            u1 = ((temp_q1 * u1) + ((double)(t)*p)) / (q1);
            u2 = (u - q1 * u1) / (1.0 - q1);
        }

        sigmab[t] = q1*q2*(u1-u2)*(u1-u2);
    }

    for(int i = 0; i < 256; i++)
    {
        if(max < sigmab[i])
        {
            max = sigmab[i];
            T = i;
        }
    }



    //Thresholding
    for(int i=0; i<igImg.Row(); i++)
    {
        for(int j=0; j<igImg.Col(); j++)
        {
            if(igImg[i][j] > T) igImg[i][j] = 255; //foreground
            else igImg[i][j] = 0; //background
        }
    }

Labeling

  • Neighbors
    • 4-Neighbors(상하좌우)
    • 8-Neighbors(상하좌우+대각선) <- Code
  • Algorithm (Binary Image)
    1. 전경인 픽셀일 경우
    2. 이전에 '확인했던' 주변을 확인
    3. 주변이 모두 배경이라면 새로운 label 부여
    4. 주변에 label이 있다면 같은 label 부여
    5. 주변의 label이 여러개인 경우 우선순위에 따라 label 부여
    6. Second Pass를 통해 좀 더 정확한 Labeling

result

Labeling

Code

//Image Labeling

    KImageGray igImg2 = igImg;
    int label[igImg2.Row()][igImg2.Col()];
    std::fill(&label[0][0], &label[igImg2.Row()-1][igImg2.Col()-1], 0);

    int label_num = 1;

    for(int i = 1; i<igImg2.Row()-1;i++)
    {
        for(int j = 1; j<igImg2.Col()-1;j++)
        {
            if(igImg2[i][j] == 255) //foreground
            {
                if( (igImg2[i-1][j-1] == 0 && igImg2[i-1][j] == 0 && igImg2[i][j-1] == 0) || (label[i-1][j-1] == 0 && label[i-1][j] == 0 && label[i][j-1] == 0)) // 대각선 위 오른쪽이 전부 배경
                {
                    label[i][j] = label_num; // label 추가
                    label_num++;
                }
                else if(igImg2[i-1][j-1] == 0 && igImg2[i-1][j] == 255 && igImg2[i][j-1] == 0) //위쪽만 전경
                {
                    label[i][j] = label[i-1][j];
                }
                else if(igImg2[i-1][j-1] == 0 && igImg2[i-1][j] == 0 && igImg2[i][j-1] == 255) //왼쪽만 전경
                {
                    label[i][j] = label[i][j-1];
                }
                else if(igImg2[i-1][j-1] == 255 && igImg2[i-1][j] == 0 && igImg2[i][j-1] == 0) //대각선만 전경
                {
                    label[i][j] = label[i-1][j-1];
                }
                else if(igImg2[i-1][j-1] == 255 && igImg2[i-1][j] == 0 && igImg2[i][j-1] == 255) //대각선이랑 왼쪽만 전경
                {
                    label[i][j] = label[i][j-1];
                    label[i-1][j] = label[i][j-1];
                }
                else if(igImg2[i-1][j-1] == 255 && igImg2[i-1][j] == 255 && igImg2[i][j-1] == 0) //대각선이랑 위쪽만 전경
                {
                    label[i][j] = label[i-1][j];
                    label[i][j-1] = label[i-1][j];
                }
                else if(igImg2[i-1][j-1] == 0 && igImg2[i-1][j] == 255 && igImg2[i][j-1] == 255) //왼쪽이랑 위쪽만 전경
                {
                    label[i][j] = label[i-1][j];
                    label[i][j-1] = label[i-1][j];
                }
                else if(igImg2[i-1][j-1] == 255 && igImg2[i-1][j] == 255 && igImg2[i][j-1] == 255) // 전부 전경
                {
                    label[i][j] = label[i-1][j]; //위쪽 , 왼쪽, 대각선 순 우선순위
                    label[i-1][j-1] = label[i-1][j];
                    label[i][j-1] = label[i-1][j];
                }
            }
        }
    }


    // Second Pass
    for(int i = 1; i<igImg2.Row()-1;i++)
    {
        for(int j = igImg2.Col()-2; j > 1;j--)
        {
            if(igImg2[i][j] == 255) //foreground
            {
                if(igImg2[i-1][j-1] == 0 && igImg2[i-1][j] == 0 && igImg2[i][j-1] == 255) //왼쪽만 전경
                {
                    label[i][j-1] = label[i][j];
                }
                else if(igImg2[i-1][j-1] == 0 && igImg2[i-1][j] == 255 && igImg2[i][j-1] == 0) //위쪽만 전경
                {
                    label[i][j] = label[i-1][j];
                }
                else if(igImg2[i-1][j-1] == 255 && igImg2[i-1][j] == 255 && igImg2[i][j-1] == 255) // 전부 전경
                {
                    label[i][j] = label[i-1][j]; //위쪽 , 왼쪽, 대각선 순 기준
                    label[i-1][j-1] = label[i-1][j];
                    label[i][j-1] = label[i-1][j];
                }

            }
        }
    }


    KImageColor imglabel = KImageColor(igImg2.Row(),igImg2.Col());

    for(unsigned int ii = 1;ii<igImg2.Row();ii++)
              for(unsigned int jj = 1;jj<igImg2.Col();jj++)
          {
              imglabel[ii][jj].b = label[ii][jj];
              imglabel[ii][jj].r = (5* label[ii][jj]) % 256 ;
              imglabel[ii][jj].g = (10* label[ii][jj]) % 256;
          }

dilation and erosion

dilation

- 팽창. mask와 겹치는 픽셀 중 한 픽셀이라도 전경이라면 현재 픽셀은 전경

erosion

- 침식. mask와 겹치는 모든 픽셀이 전경이어야만 현재 픽셀이 전경, 아니라면 배경

result

3x3


3x3

5x5


5x5

Code (3x3, 5x5)

    ImageForm* q_pForm = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Otsu's Thresholding")
            {
                q_pForm = (*_plpImageForm)[i];
                break;
            }



        KImageGray img_dil = q_pForm->ImageGray();
        KImageGray img_ero = q_pForm->ImageGray();
        KImageGray img_tmp = q_pForm->ImageGray();


        //Dilation
        for(unsigned int i = 1;i<img_dil.Row()-1;i++)
        {
            for(unsigned int j = 1;j<img_dil.Col()-1;j++)
            {
                if(img_tmp[i-1][j-1] == 255 || img_tmp[i-1][j] == 255 || img_tmp[i-1][j+1] == 255 || img_tmp[i][j-1] == 255 || img_tmp[i][j+1] == 255 || img_tmp[i+1][j-1] == 255 || img_tmp[i+1][j] == 255 || img_tmp[i+1][j+1] == 255 )
                    img_dil[i][j] = 255;
            }

        }

        //Eroison
        for(unsigned int i = 1;i<img_ero.Row()-1;i++)
        {
            for(unsigned int j = 1;j<img_ero.Col()-1;j++)
            {
                if(img_tmp[i-1][j-1] == 0 || img_tmp[i-1][j] == 0 || img_tmp[i-1][j+1] == 0 || img_tmp[i][j-1] == 0 || img_tmp[i][j+1] == 0 || img_tmp[i+1][j-1] == 0 || img_tmp[i+1][j] == 0 || img_tmp[i+1][j+1] == 0 )
                    img_ero[i][j] = 0;
            }

        }
    ImageForm* q_pForm = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Otsu's Thresholding")
            {
                q_pForm = (*_plpImageForm)[i];
                break;
            }

        KImageGray img_dil = q_pForm->ImageGray();
        KImageGray img_ero = q_pForm->ImageGray();
        KImageGray img_tmp = q_pForm->ImageGray();


        //Dilation
        for(unsigned int i = 2;i<img_dil.Row()-2;i++)
        {
            for(unsigned int j = 2;j<img_dil.Col()-2;j++)
            {
                if(img_tmp[i-2][j-2] == 255 || img_tmp[i-2][j-1] == 255|| img_tmp[i-2][j] == 255 || img_tmp[i-2][j+1] == 255 || img_tmp[i-2][j+2] == 255 ||
                   img_tmp[i-1][j-2] == 255 || img_tmp[i-1][j-1] == 255|| img_tmp[i-1][j] == 255 || img_tmp[i-1][j+1] == 255 || img_tmp[i-1][j+2] == 255 ||
                   img_tmp[i][j-2] == 255 || img_tmp[i][j-1] == 255 || img_tmp[i][j+1] == 255 || img_tmp[i][j+2] == 255 ||
                   img_tmp[i+1][j-2] == 255 || img_tmp[i+1][j-1] == 255|| img_tmp[i+1][j] == 255 || img_tmp[i+1][j+1] == 255 || img_tmp[i+1][j+2] == 255 ||
                   img_tmp[i+2][j-2] == 255 || img_tmp[i+2][j-1] == 255|| img_tmp[i+2][j] == 255 || img_tmp[i+2][j+1] == 255 || img_tmp[i+2][j+2] == 255)
                    img_dil[i][j] = 255;
            }

        }

        //Eroison
        for(unsigned int i = 2;i<img_ero.Row()-2;i++)
        {
            for(unsigned int j = 2;j<img_ero.Col()-2;j++)
            {
                if(img_tmp[i-2][j-2] == 0 || img_tmp[i-2][j-1] == 0|| img_tmp[i-2][j] == 0 || img_tmp[i-2][j+1] == 0 || img_tmp[i-2][j+2] == 0 ||
                   img_tmp[i-1][j-2] == 0 || img_tmp[i-1][j-1] == 0|| img_tmp[i-1][j] == 0 || img_tmp[i-1][j+1] == 0 || img_tmp[i-1][j+2] == 0 ||
                   img_tmp[i][j-2] == 0 || img_tmp[i][j-1] == 0 || img_tmp[i][j+1] == 0 || img_tmp[i][j+2] == 0 ||
                   img_tmp[i+1][j-2] == 0 || img_tmp[i+1][j-1] == 0|| img_tmp[i+1][j] == 0 || img_tmp[i+1][j+1] == 0 || img_tmp[i+1][j+2] == 0 ||
                   img_tmp[i+2][j-2] == 0 || img_tmp[i+2][j-1] == 0|| img_tmp[i+2][j] == 0 || img_tmp[i+2][j+1] == 0 || img_tmp[i+2][j+2] == 0 )
                    img_ero[i][j] = 0;
            }

        }

Input Image

Input

Color

color

Sepia Tone Transform Algorithm

1. Transform RGB color to HSV color
2. Change H and S
3. Transform HSV color to RGB

sepia_tone_transform

result

  • HUE : 110, SAT : 0.3

sepia_resultHSV

Code

void MainFrame::on_buttonSepiaTone_clicked()
{
    KImageColor   icMain;

    //포커스 된 ImageForm으로부터 영상을 가져옴

    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageColor().Address() &&  _q_pFormFocused->ID() == "OPEN")
        icMain = _q_pFormFocused->ImageColor();
    else
        return;

    KImageGray img_Hue(icMain.Row(),icMain.Col()),
               img_Sat(icMain.Row(),icMain.Col()),
               img_Val(icMain.Row(),icMain.Col());

    //hue, saturation 값 가져오기
    double dHue2 = ui->spinHue->value();//text().toDouble();
    double dSat2 = ui->spinSaturation->value(); // 위와 같은 식으로

    //
    //icMain 변환
    //:

    double dMin, dMax, dDiff;
    double dVal;
    double C,X,m;
    double R,G,B;
    double dHue,dSat;

    for(int i=icMain.Row(),ii=0; i; i--,ii++)
        for(int j=icMain.Col(),jj=0; j; j--,jj++)
        {
            dMin  = std::min(std::min(icMain[ii][jj].r,icMain[ii][jj].g),icMain[ii][jj].b);
            dMax  = std::max(std::max(icMain[ii][jj].r,icMain[ii][jj].g),icMain[ii][jj].b);
            dDiff = dMax - dMin;

            //value
            dVal = dMax/255.0;
            img_Val[ii][jj]= dVal*255;

            //saturation
            dSat = dDiff/dMax;

            //hue
            if(dMax == (double)(icMain[ii][jj].r)) //maximun = red
                dHue = 60.0 * (double)(icMain[ii][jj].g-icMain[ii][jj].b)/dDiff;
            else if(dMax == (double)(icMain[ii][jj].g))
                dHue = 60.0 * (double)(icMain[ii][jj].b-icMain[ii][jj].r)/dDiff + 120.0;
            else
                dHue = 60.0 * (double)(icMain[ii][jj].r-icMain[ii][jj].g)/dDiff + 240.0;

            if(dHue == 360.0)
                dHue = 0.0;

            img_Hue[ii][jj]=dHue/360.0 * 255;
            img_Sat[ii][jj]=dSat * 255.0;

            dHue = dHue2;
            dSat = dSat2;

            C = dVal*dSat;
            X = C*(1-std::fabs(fmod(dHue/60,2)-1));
            m = dVal - C;

            if(dHue>=0&&dHue<60)
            {
                R = C;
                G = X;
                B = 0;
            }
            else if (dHue>=60&&dHue<120)
            {
                R = X;
                G = C;
                B = 0;
            }
            else if (dHue>=120&&dHue<180)
            {
                R = 0;
                G = C;
                B = X;
            }
            else if (dHue>=180&&dHue<240)
            {
                R = 0;
                G = X;
                B = C;
            }
            else if (dHue>=240&&dHue<300)
            {
                R = X;
                G = 0;
                B = C;
            }
            else if (dHue>=300&&dHue<360)
            {
                R = C;
                G = 0;
                B = X;
            }


            icMain[ii][jj].r=(R+m)*255.0;
            icMain[ii][jj].g=(G+m)*255.0;
            icMain[ii][jj].b=(B+m)*255.0;

        }

    //출력을 위한 ImageForm 생성
    //sepia
    ImageForm*  q_pForm = new ImageForm(icMain, "Sepia Tone", this);
    _plpImageForm->Add(q_pForm);
    q_pForm->show();


    //HSV
    ImageForm*  q_pHue = new ImageForm(img_Hue,"Hue",this);
    _plpImageForm->Add(q_pHue);
    q_pHue->show();

    ImageForm*  q_pSat = new ImageForm(img_Sat,"Sat",this);
    _plpImageForm->Add(q_pSat);
    q_pSat->show();

    ImageForm*  q_pVal = new ImageForm(img_Val,"Val",this);
    _plpImageForm->Add(q_pVal);
    q_pVal->show();


    //UI 활성화 갱신
    UpdateUI();
}

+ Recent posts