[图像处理]边缘提取以及Harris角点检测

在本周的计算机视觉与模式识别作业中,给定输入图像是两张普通A4打印纸,上面可能有手写笔记或者打印内容但是拍照时角度不正。要求输出:
1. 图像的边缘;
2. 计算 A4纸边缘的各直线方程;
3. 提取A4纸的4个角点。
[图像处理]边缘提取以及Harris角点检测_第1张图片
[图像处理]边缘提取以及Harris角点检测_第2张图片


作业要求的是使用C++的CImg库,与OpenCV和MATLAB相比,CImg还是有点不太方便。

  • 图像边缘的提取
    这个任务比较简单,找一个像Prewitt算子、拉普拉斯算子或是其他算子,对输入图像进行卷积计算就可以了,原理是计算图像像素的变化,而变化剧烈的地方就是图像边缘出现的地方。
    这里我使用了Sobel算子来进行边缘提取并二值化。
    int sobelX[3][3] = { { -1,0,1 },{ -2,0,2 },{ -1,0,1 } };
    int sobelY[3][3] = { { 1,2,1 },{ 0,0,0 },{ -1,-2,-1 } };
    Sobel算子就是分别求图像X、Y方向的梯度,再求梯度平方和,小于某个阈值置为0,反之为255。
    最后结果如下:
    [图像处理]边缘提取以及Harris角点检测_第3张图片
    [图像处理]边缘提取以及Harris角点检测_第4张图片
  • Harris角点的检测
    主要时间都花费在这部分的实现上面了。这里推荐一篇文章,里面详解了Harris角点检测算法的原理、步骤,还给出了OpenCV实现的代码。
    http://www.cnblogs.com/ronny/p/4009425.html
    还有一些有帮助的文章:
    http://blog.csdn.net/l_inyi/article/details/8915116
    http://blog.csdn.net/berguiliu/article/details/24985825
    关键步骤如下:
    [图像处理]边缘提取以及Harris角点检测_第5张图片
    而我自己则是在理解该文章所写算法的基础上,使用CImg库按步骤地搜索网上的资料,完成每一部分之后,检测出了角点。
    在实现过程中遇到的问题主要有:
    • 在求x、y方向梯度的时候,一开始使用的是一位的模板,导致检测到的角点过多,容易混淆。后来经过实验,改成了二维的3X3模板,效果较好;
    • 在第三步使用高斯加权的时候,我以为是对各梯度矩阵使用高斯分布的公式:这里写图片描述
      角点检测的结果:[图像处理]边缘提取以及Harris角点检测_第6张图片
      明显不太对。
      后来查阅资料才认识到应该使用高斯低通滤波窗口对Ixx、Iyy、Ixy进行卷积。更正之后的结果:
      [图像处理]边缘提取以及Harris角点检测_第7张图片[图像处理]边缘提取以及Harris角点检测_第8张图片
  • 求边缘直线方程
    在Harris角点检测所得的图片当中,可以看到四个角的角点是独立出来的,能够比较容易地提取它们的坐标。因此可以利用Harris角点检测过程中计算的result数组来找到四个顶点的坐标,有了坐标之后四条直线的方程也就知道了。
    最后求得的方程:
    [图像处理]边缘提取以及Harris角点检测_第9张图片

差点忘了贴代码:

// CV_HW2.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include 
#include "CImg.h"
using namespace cimg_library;
using namespace std;

/*

CImg<> edgeDetect(CImg<> src) {
    int mask[3][3] = { {0,-1,0},{-1,4,-1},{0,-1,0} };
    int width = src.width();
    int height = src.height();
    CImg newImg(width, height, 1, 3);
    newImg.fill(0);
    for (int i = 1; i < width - 1; i++) {
        for (int j = 1; j < height - 1; j++) {

            for (int k = 0; k < 3; k++) {
                double sum = 0.0;
                for (int row = 0; row < 3; row++) {
                    for (int col = 0; col < 3; col++) {
                        sum += mask[row][col] * src(i - 1 + row, j - 1 + col, k);
                    }
                }
                if (sum < 0)
                    sum = 0;
                else if (sum > 255)
                    sum = 255;
                newImg(i, j, k) = sum;
            }
        }
    }
    newImg.display();
    return newImg;
}
*/

CImg<> edgeDetect(CImg<> src) {
    int sobelX[3][3] = { { -1,0,1 },{ -2,0,2 },{ -1,0,1 } };
    int sobelY[3][3] = { { 1,2,1 },{ 0,0,0 },{ -1,-2,-1 } };
    int width = src.width();
    int height = src.height();
    CImg<double> G(width, height, 1, 3);
    CImg<double> newImg(width, height, 1, 3);
    newImg.fill(0);
    for (int i = 1; i < width - 1; i++) {
        for (int j = 1; j < height - 1; j++) {
            double sumX = 0.0;
            double sumY = 0.0;
            for (int row = 0; row < 3; row++) {
                for (int col = 0; col < 3; col++) {
                    sumX += sobelX[row][col] * src(i - 1 + row, j - 1 + col, 0);
                    sumY += sobelY[row][col] * src(i - 1 + row, j - 1 + col, 0);
                }
            }

            newImg(i, j, 0) = sqrt(sumX*sumX + sumY*sumY) > 85 ? 255 : 0;
            newImg(i, j, 1) = newImg(i, j, 0);
            newImg(i, j, 2) = newImg(i, j, 0);
        }
    }
    newImg.display();
    //newImg.dilate(3.5);
    return newImg;
}

void print(double k, double b) {
    cout << "y = " << k << " * " << "x";
    if (b > 0) {
        cout << "+" << b;
    }
    else if (b < 0) {
        cout << b;
    }
    cout << endl;
}

void line(double x1, double y1, double x2, double y2) {
    double k = (y1 - y2) / (x1 - x2);
    double b =y1 - k*x1;
    print(k, b);
}

CImg<> pointDetect(CImg<> src) {
    int width = src.width();
    int height = src.height();

    double upLeftX = 0.0;
    double upLeftY = 0.0;
    double downLeftX = 0.0;
    double downLeftY = 0.0;
    double upRightX = 0.0;
    double upRightY = 0.0;
    double downRightX = 0.0;
    double downRightY = 0.0;
    //求左上顶点

    for (int j = 100; j < height - 50; j++) {
        for (int i = 100; i < width; i++) {

            if (upLeftX == 0 && upLeftY == 0 && src(i, j, 0) == 1) {
                upLeftX = i;
                upLeftY = j;
                break;
            }
        }
        if (upLeftX || upLeftY) {
            break;
        }
    }
    //求右上顶点

    for (int i = width - 50; i >= 0; i--) {
        for (int j = 100; j < height; j++) {
            if (upRightX == 0 && upRightY == 0 && src(i, j,0) == 1) {
                upRightX = i;
                upRightY = j;
                break;
            }
        }
        if (upRightX || upRightY) {
            break;
        }
    }
    //求左下顶点

    for (int j = height - 110; j >= 0; j--) {
        for (int i = width-100; i >= 0; i--) {
            if (downLeftX == 0 && downLeftY == 0 && src(i, j, 0) == 1) {
                downLeftX = i;
                downLeftY = j;
                break;
            }
        }
        if (downLeftX || downLeftY) {
            break;
        }
    }
    //求右下顶点
    for (int j = downLeftY-1; j >= 0; j--) {
        for (int i = width - 50; i >= 0; i--) {
            if (downRightX == 0 && downRightY == 0 && src(i, j, 0) == 1
                && abs((i - downLeftX) - (upRightX - upLeftX)) < 60) {
                downRightX = i;
                downRightY = j;
                break;
            }
        }
        if (downRightX && downRightY) {
            break;
        }
    }

    cout << "图像的四个角点坐标分别是:" << endl;
    cout << "左上角upLeft:(" << upLeftX << "," << upLeftY  << ")"<< endl;
    cout << "右上角upRight:(" << upRightX << " ," << upRightY << ")" << endl;
    cout << "右下角downRight:(" << downRightX << " ," << downRightY << ")" << endl;
    cout << "左下角downLeft:(" << downLeftX << " ," << downLeftY << ")" << endl;

    cout << "上方直线方程为:";
    line(upLeftX, upLeftY, upRightX, upRightY);

    cout << "左方直线方程为:";
    line(upLeftX, upLeftY, downLeftX, downLeftY);

    cout << "右方直线方程为:";
    line(upRightX, upRightY, downRightX, downRightY);

    cout << "下方直线方程为:";
    line(downLeftX, downLeftY, downRightX, downRightY);
    //src.dilate(3);
    //src.display();
    return src;
}


CImg<> harrisCorners(CImg<> src) {
    int width = src.width();
    int height = src.height();

    int horizontal[3][3] = { { 5,0, -5 },{ 8,0,-8 }, { 5,0,-5 } };
    int vertical[3][3] = { { 5,8, 5 },{ 0,0,0 },{ -5,-8,-5 } };

    CImg<double> Ix(width, height, 1, 3);
    CImg<double> Iy(width, height, 1, 3);

    CImg<double> Ixx(width, height, 1, 3);
    CImg<double> Iyy(width, height, 1, 3);
    CImg<double> Ixy(width, height, 1, 3);

    //x、y方向梯度
    for (int i = 1; i < width; i++) {
        for (int j = 1; j < height - 1; j++) {
            for (int k = 0; k < 3; k++) {
                double sumX = 0.0;
                double sumY = 0.0;
                for (int row = 0; row < 3; row++) {
                    for (int col = 0; col < 3; col++) {
                        sumX += src(row + i - 1, col + j - 1, k)*horizontal[row][col];
                        sumY += src(row + i - 1, col + j - 1, k)*vertical[row][col];
                    }
                }
                Ix(i, j, k) = sumX;
                Iy(i, j, k) = sumY;
            }
        }
    }

    //两个方向梯度的乘积
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            for (int k = 0; k < 3; k++) {
                Ixx(i, j, k) = Ix(i, j, k)*Ix(i, j, k);
                Iyy(i, j, k) = Iy(i, j, k)*Iy(i, j, k);
                Ixy(i, j, k) = Ix(i, j, k)*Iy(i, j, k);
            }
        }
    }
    //高斯加权
    CImg<double> R(width, height, 1, 3);
    double pi = 3.1415926;
    double alpha = 0.04;
    double maxR[3] = { INT_MIN };
    double threshHold[3];
    int gauss[3][3] = { {1,2,1},{2,4,2},{1,2,1} };
    for (int i = 1; i < width-1; i++) {
        for (int j = 1; j < height-1; j++) {
            for (int k = 0; k < 3; k++) {
                double A = 0.0;
                double B = 0.0;
                double C = 0.0;
                for (int row = 0; row < 3; row++) {
                    for (int col = 0; col < 3; col++) {
                        A += Ixx(row + i - 1, col + j - 1, k)*gauss[row][col];
                        B += Ixy(row + i - 1, col + j - 1, k)*gauss[row][col];
                        C += Iyy(row + i - 1, col + j - 1, k)*gauss[row][col];
                    }
                }
                A /= 16.0;
                B /= 16.0;
                C /= 16.0;
                //double A = (Ixx(i - 1, j - 1, k) + Ixx(i - 1, j + 1, k) + Ixx(i + 1, j - 1, k) + Ixx(i + 1, j + 1, k) + (Ixx(i - 1, j, k) + Ixx(i, j - 1, k) + Ixx(i + 1, j, k) + Ixx(i, j + 1, k)) * 2.0 + Ixx(i, j, k) * 4) / 16;
                //double A = exp(-Ixx(i, j, k)*Ixx(i, j, k) / 8.0) / (2.0 * sqrt(2.0 * pi));
                //double B = exp(-Ixy(i, j, k)*Ixy(i, j, k) / 8.0) / (2.0 * sqrt(2.0 * pi));
                //double C = exp(-Iyy(i, j, k)*Iyy(i, j, k) / 8.0) / (2.0 * sqrt(2.0 * pi));

                //Harris响应值R
                R(i, j, k) = (A*C - B*B) - alpha*(A + C)*(A + C);
                if (maxR[k] < R(i, j, k)) {
                    maxR[k] = R(i, j, k);
                    threshHold[k] = 0.01*maxR[k];
                }
            }
        }
    }
    //小于阈值的R置为0
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            for (int k = 0; k < 3; k++) {
                if (R(i, j, k) < threshHold[k]) {
                    R(i, j, k) = 0;
                }
            }
        }
    }
    CImg<double> result(width, height, 1, 3);
    result.fill(0);
    for (int i = 1; i < width - 1; i++) {
        for (int j = 1; j < height - 1; j++) {
            for (int k = 0; k < 3; k++) {
                //局部非最大值抑制
                if (R(i, j, k) > threshHold[k] && R(i, j, k) > R(i - 1, j - 1, k) && R(i, j, k) > R(i - 1, j, k) && R(i, j, k) > R(i - 1, j + 1, k)
                    && R(i, j, k) > R(i, j - 1, k) && R(i, j, k) > R(i, j + 1, k) && R(i, j, k) > R(i + 1, j - 1, k) && R(i, j, k) > R(i + 1, j, k)
                    && R(i, j, k) > R(i + 1, j + 1, k)) {
                    result(i, j, k) = 1;
                }
            }
        }
    }
    pointDetect(result);
    unsigned char color[] = { 255,255,0 };
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            if (result(i, j, 0) == 1) {
                src.draw_circle(i, j, 5, color);
            }
        }
    }
    src.display();
    return src;
}

int main()
{
    CImg<> src1("1.bmp");
    CImg<> tmp = edgeDetect(src1);
    tmp.save_bmp("edge1.bmp");
    CImg<> tmp2 = harrisCorners(src1);
    tmp2.save_bmp("harris1.bmp");

    CImg<> src("2.bmp");
    CImg<> tmp3 = edgeDetect(src);
    tmp3.save_bmp("edge2.bmp");
    CImg<> tmp5 = harrisCorners(src);
    tmp5.save_bmp("harris2.bmp");
    return 0;
}

你可能感兴趣的:(图像处理)