Ex4:Image Warping and Image Morphing

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_31790817/article/details/80278715

Ex4:Image Warping and Image Morphing

完整代码见github地址:https://github.com/linjiafengyang/ComputerVision

作业4

测试数据

普通A4打印纸,上面可能有手写笔记或者打印内容,但是拍照时可能角度不正。(参考Dataset2)。
输出:
已经矫正好的标准普通A4纸(长宽比为A4纸的比例),并裁掉无用的其他内容,只保留完整A4纸张。
实验数据集依旧使用作业3的数据集。

说明:尽量保留A4纸上的原有信息不变或减少图像信息(原图像内容不要做缩放,A4纸的面积基本不变),以利于后期的文字识别和处理。

测试结果

以下面两张图为例:
这里写图片描述

这里写图片描述

代码文件

在作业3的基础上,先求A4纸的四个顶点,再进行Warping为标准普通A4纸,最后裁剪。

ImageWarping.h文件:

#include "CImg.h"
#include <iostream>
#include <vector>
using namespace cimg_library;
using namespace std;

struct Point {
    double x, y;
    int cnt;
    Point(double _x, double _y, int _cnt): x(_x), y(_y), cnt(_cnt) {}
};
struct Line {
    double k, b;
    Line(double _k, double _b): k(_k), b(_b) {}
    int index;
    double distance;
    Line(int _index, double _distance): index(_index), distance(_distance) {}
};
class Hough {
private:
    CImg<float> grayImage; // 灰度图
    CImg<float> blurred_img; // 高斯滤波平滑得到的图
    CImg<float> houghspace; // 霍夫空间图
    CImg<float> hough_result; // 霍夫检测图
    CImg<float> A4; // a4纸结果图
    vector<Point> peaks; // 霍夫空间直线经过最多的点
    vector<Line> lines; // 直线
    vector<Point> intersections; // 直线交点

    double sigma;
    double gradient_threshold;
    double vote_threshold;
    double peak_dis;
    int x_min, x_max, y_min, y_max;
public:
    Hough(CImg<float> srcImg, double sigma, double gradient_threshold, double vote_threshold, double peak_dis);
    CImg<float> imageWarping(CImg<float> srcImg);
    CImg<float> RGBtoGray(const CImg<float>& srcImg); // 转灰度图
    CImg<float> initHoughSpace(); // 初始化霍夫空间
    void findPeaks(); // 投票算法
    void drawLines(); // 寻找并画出直线
    void drawIntersections(); // 寻找并画出直线交点
    vector<CImg<float> > computeTransformMatrix(CImg<float> a4); // 计算变换矩阵
    CImg<float> warping(CImg<float> srcImg); // image warping
};

ImageWarping.cpp文件:

#include "ImageWarping.h"

Hough::Hough(CImg<float> srcImg, double sigma, double gradient_threshold, double vot_threshold, double peak_dis) {
    this->hough_result = srcImg;
    this->sigma = sigma;
    this->gradient_threshold = gradient_threshold;
    this->vote_threshold = vot_threshold;
    this->peak_dis = peak_dis;
    this->x_min = 0;
    this->x_max = srcImg._width - 1; // 图像宽度
    this->y_min = 0;
    this->y_max = srcImg._height - 1; // 图像高度
}
CImg<float> Hough::imageWarping(CImg<float> srcImg) {
    this->grayImage = RGBtoGray(srcImg); // 转灰度图
    this->blurred_img = grayImage.get_blur(sigma); // 高斯滤波平滑
    this->houghspace = initHoughSpace(); // 初始化霍夫空间
    findPeaks(); // 找出霍夫空间中直线经过最多的点
    drawLines(); // 寻找并画出直线
    drawIntersections(); // 寻找并画出直线交点
    //hough_result.display();
    this->A4 = warping(srcImg); // image warping
    return A4;
}
// 转灰度图
CImg<float> Hough::RGBtoGray(const CImg<float>& srcImg) {
    CImg<float> grayImage = CImg<float>(srcImg._width, srcImg._height, 1, 1, 0);
    cimg_forXY(grayImage, x, y) {
        grayImage(x, y, 0) = (int)round((double)srcImg(x, y, 0, 0) * 0.299 + 
                                    (double)srcImg(x, y, 0, 1) * 0.587 + 
                                    (double)srcImg(x, y, 0, 2) * 0.114);
    }
    return grayImage;
}
// 初始化霍夫空间
CImg<float> Hough::initHoughSpace() {
    CImgList<float> gradient =  blurred_img.get_gradient("xy", 2);

    CImg<float> gradient_x = gradient[0]; // x方向上的梯度
    CImg<float> gradient_y = gradient[1]; // y方向上的梯度

    int maxp = (int)sqrt(grayImage._width*grayImage._width + grayImage._height*grayImage._height);
    CImg<float> hough_space(360, maxp, 1, 1, 0); // 初始化hough space

    cimg_forXY(grayImage, i, j) {
        double grad = sqrt(gradient_x(i, j)*gradient_x(i, j) + gradient_y(i, j)*gradient_y(i, j));
        if (grad > gradient_threshold) {
            grayImage(i, j) = grad;
            cimg_forX(hough_space, alpha) {
                double theta = ((double)alpha*cimg::PI) / 180;
                int p = (int)(i*cos(theta) + j*sin(theta));
                if (p >= 0 && p < maxp) {
                    hough_space(alpha, p)++; // 累加矩阵
                }
            }
        }
    }
    return hough_space;
}
// 投票算法找出霍夫空间中直线经过最多的点
void Hough::findPeaks() {
    peaks.clear();
    cimg_forXY(houghspace, theta, p) {
        if (houghspace(theta, p) > vote_threshold) {
            bool flag = true;
            double alpha = (double)theta*cimg::PI / 180;
            // y的范围
            const int y0 = ((double)p / (sin(alpha))) - double(x_min)*(1 / tan(alpha));
            const int y1 = ((double)p / (sin(alpha))) - double(x_max)*(1 / tan(alpha));
            // x的范围
            const int x0 = ((double)p / (cos(alpha))) - double(y_min)*(tan(alpha));
            const int x1 = ((double)p / (cos(alpha))) - double(y_max)*(tan(alpha));

            if (x0 >= x_min && x0 <= x_max || x1 >= x_min && x1 <= x_max ||
                y0 >= y_min && y0 <= y_max || y1 >= y_min && y1 <= y_max) {
                for (int i = 0; i < peaks.size(); i++) {
                    if (sqrt((peaks[i].x - theta)*(peaks[i].x - theta) 
                        + (peaks[i].y - p)*(peaks[i].y - p)) < peak_dis) {
                        flag = false;
                        if (peaks[i].cnt < houghspace(theta, p)) {
                            Point temp(theta, p, houghspace(theta, p));
                            peaks[i] = temp;
                        }
                    }
                }
                if (flag) {
                    Point temp(theta, p, houghspace(theta, p));
                    peaks.push_back(temp);
                }
            }
        }
    }
}
// 寻找并画出直线
void Hough::drawLines() {
    lines.clear();
    for (int i = 0; i < peaks.size(); i++) {
        double theta = double(peaks[i].x)*cimg::PI / 180;
        double k = -cos(theta) / sin(theta); // 直线斜率
        double b = double(peaks[i].y) / sin(theta);
        Line templine(k, b);
        lines.push_back(templine);
        cout << "Line " << i << ": y = " << k << "x + " << b << endl;
    }

    const double lines_color[] = { 255, 0, 0 };
    for (int i = 0; i < lines.size(); i++) {
        const int x0 = (double)(y_min - lines[i].b) / lines[i].k;
        const int x1 = (double)(y_max - lines[i].b) / lines[i].k;
        const int y0 = x_min*lines[i].k + lines[i].b;
        const int y1 = x_max*lines[i].k + lines[i].b;

        if (abs(lines[i].k) > 1) {
            hough_result.draw_line(x0, y_min, x1, y_max, lines_color);
        }
        else {
            hough_result.draw_line(x_min, y0, x_max, y1, lines_color);
        }
    }

}
bool sortByDistance(Line a, Line b) {
    return a.distance < b.distance;
}
// 寻找并画出直线交点
void Hough::drawIntersections() {
    vector<Point> tempIntersections;
    int k = 0;
    for (int i = 0; i < lines.size(); i++) {
        for (int j = i + 1; j < lines.size(); j++) {
            double k0 = lines[i].k, k1 = lines[j].k;
            double b0 = lines[i].b, b1 = lines[j].b;

            double x = (b1 - b0) / (k0 - k1);
            double y = (k0*b1 - k1*b0) / (k0 - k1);

            if (x >= 0 && x < grayImage._width && y >= 0 && y < grayImage._height) {
                Point tempPoint(x, y, 0);
                tempIntersections.push_back(tempPoint);
                cout << "Intersection " << k++ << ": x = " << x << ", y = " << y << endl;
            }
        }
    }

    const double intersections_color[] = { 255, 0, 0 };
    vector<Line> sort_line;
    for (int i = 0; i < tempIntersections.size(); i++) {
        hough_result.draw_circle(tempIntersections[i].x, tempIntersections[i].y, 50, intersections_color);

        double temp_x = tempIntersections[i].x - tempIntersections[0].x;
        double temp_y = tempIntersections[i].y - tempIntersections[0].y;
        double dis = sqrt(temp_x*temp_x + temp_y*temp_y);
        Line temp(i, dis);
        sort_line.push_back(temp);
    }
    sort(sort_line.begin(), sort_line.end(), sortByDistance);
    intersections.clear();
    for (int i = 0; i < sort_line.size(); i++) {
        intersections.push_back(tempIntersections[sort_line[i].index]); // 四个角点
    }
}
// 计算变换矩阵
vector<CImg<float> > Hough::computeTransformMatrix(CImg<float> a4) {
    vector<Point> destRectPoints;
    Point tempPoint1(0, 0, 0);
    Point tempPoint2(a4._width - 1, 0, 0);
    Point tempPoint3(0, a4._height - 1, 0);
    Point tempPoint4(a4._width - 1, a4._height - 1, 0);
    destRectPoints.push_back(tempPoint1); destRectPoints.push_back(tempPoint2);
    destRectPoints.push_back(tempPoint3); destRectPoints.push_back(tempPoint4);

    CImg<float> y1(1, 3, 1, 1, 0), y2(1, 3, 1, 1, 0), y3(1, 3, 1, 1, 0), y4(1, 3, 1, 1, 0);
    CImg<float> c1(1, 3, 1, 1, 0), c2(1, 3, 1, 1, 0), c3(1, 3, 1, 1, 0), c4(1, 3, 1, 1, 0);
    CImg<float> A1(3, 3, 1, 1, 1), A2(3, 3, 1, 1, 1);

    for (int i = 0; i < 3; i++) {
        A1(0, i) = destRectPoints[i].x; A1(1, i) = destRectPoints[i].y;
        A2(0, i) = destRectPoints[3-i].x; A2(1, i) = destRectPoints[3-i].y;

        y1(0, i) = intersections[i].x; y2(0, i) = intersections[i].y;
        y3(0, i) = intersections[3-i].x; y4(0, i) = intersections[3-i].y;
    }
    c1 = y1.solve(A1); c2 = y2.solve(A1);
    c3 = y3.solve(A2); c4 = y4.solve(A2);

    CImg<float> temptransform1(3, 3, 1, 1, 0), temptransform2(3, 3, 1, 1, 0);
    for (int i = 0; i < 3; i++) {
        temptransform1(i, 0) = c1(0, i);
        temptransform1(i, 1) = c2(0, i);

        temptransform2(i, 0) = c3(0, i);
        temptransform2(i, 1) = c4(0, i);
    }
    temptransform1(0, 2) = 0; temptransform1(1, 2) = 0; temptransform1(2, 2) = 1;
    temptransform2(0, 2) = 0; temptransform2(1, 2) = 0; temptransform2(2, 2) = 1;
    vector<CImg<float> > temptransform;
    temptransform.push_back(temptransform1);
    temptransform.push_back(temptransform2);
    return temptransform;
}
// image warping
CImg<float> Hough::warping(CImg<float> srcImg) {
    // 当分辨率是150像素/英寸时,A4纸像素长宽分别是1754×1240
    // 当分辨率是300像素/英寸时,A4纸像素长宽分别是3508×2479
    CImg<float> a4(1240, 1754, 1, 3, 0);
    //CImg<float> a4(2479, 3508, 1, 3, 0);
    vector<CImg<float> > transform;
    transform = computeTransformMatrix(a4); // 计算变换矩阵

    CImg<float> y(1, 2, 1, 1, 0);
    CImg<float> c(1, 2, 1, 1, 0);
    CImg<float> A(2, 2, 1, 1, 1);
    A(0, 0) = 0;
    A(0, 1) = a4._width - 1;
    y(0, 0) = a4._height - 1;
    y(0, 1) = 0;
    c = y.solve(A);

    CImg<float> temp1(1, 3, 1, 1, 1), temp2(1, 3, 1, 1, 1);
    cimg_forXY(a4, i, j) {
        temp1(0, 0) = i;
        temp1(0, 1) = j;

        double inner_procuct = i * c(0, 0) - j + c(0, 1);
        temp2 = inner_procuct >= 0 ? transform[0] * temp1 : transform[1] * temp1;
        temp2(0, 0) = temp2(0, 0) < 0 ? 0 : (temp2(0, 0) > x_max ? x_max : temp2(0, 0));
        temp2(0, 1) = temp2(0, 1) < 0 ? 0 : (temp2(0, 1) > y_max ? y_max : temp2(0, 1));

        a4(i, j, 0) = srcImg(temp2(0, 0), temp2(0, 1), 0);
        a4(i, j, 1) = srcImg(temp2(0, 0), temp2(0, 1), 1);
        a4(i, j, 2) = srcImg(temp2(0, 0), temp2(0, 1), 2);
    }
    return a4;
}

main.cpp文件:

#include "ImageWarping.cpp"
#include <time.h>
#include <iostream>
int main() {
    CImg<float> src("Dataset/6.bmp");
    // 4.bmp需要将sigma设为5.5f
    // args: src sigma gradient_threshold vote_threshold peak_dis
    Hough hough(src, 10.5f, 30, 1000, 60);
    CImg<float> result = hough.imageWarping(src);
    result.display();
    result.save("result/6.bmp");
    return 0;
}

附加题

这里写图片描述

这里写图片描述

扫描二维码关注公众号,回复: 4040038 查看本文章

根据Image Morphing方法完成中间11帧的差值,得到一个Image Morphing的动画视频。

测试结果

见github地址:https://github.com/linjiafengyang/ComputerVision

代码文件

ImageMorphing.h文件:

#include <iostream>
#include "CImg.h"
#include <vector>
#include <string>

using namespace std;
using namespace cimg_library;

struct Point {
    float x, y;
    Point(float _x, float _y): x(_x), y(_y) {}
};
struct Triangle {
    Point p1, p2, p3;
    Triangle(Point _p1, Point _p2, Point _p3): p1(_p1), p2(_p2), p3(_p3) {}
};
class ImageMorphing {
private:
    vector<Point> src_points;
    vector<Point> dst_points;
    vector<vector<Point> > mid_points;

    vector<vector<int> > index;
    vector<Triangle> src_triangle_list;
    vector<Triangle> dst_triangle_list;
    vector<vector<Triangle> > mid_triangle_list;

    CImg<float> src;
    CImg<float> dst;
    CImgList<float> result;

    int frame_cnt;
public:
    ImageMorphing(CImg<float> src, CImg<float> dst, vector<Point> src_points, vector<Point> dst_points, 
                    vector<vector<int> > index, int frame_cnt);
    void setSrcTriangleList();
    void setDstTriangleList();
    void setMidTriangleList();
    CImg<float> computeTransformMatrix(Triangle before, Triangle after); // 计算变换矩阵
    bool isInTriangle(Point P, Triangle tri); // 重心法判断一个点是否在三角形内
    CImgList<float> morphing();
};

ImageMorphing.cpp文件:

#include "ImageMorphing.h"

ImageMorphing::ImageMorphing(CImg<float> src, CImg<float> dst, vector<Point> src_points, 
                vector<Point> dst_points, vector<vector<int>> index, int frame_cnt) {
    this->src = src;
    this->dst = dst;
    this->src_points = src_points;
    this->dst_points = dst_points;
    this->index = index;
    this->frame_cnt = frame_cnt;
    setSrcTriangleList();
    setDstTriangleList();
    setMidTriangleList();
}
// src图的三角形
void ImageMorphing::setSrcTriangleList() {
    for (int i = 0; i < index.size(); i++) {
        Triangle src_triangle(src_points[index[i][0]], src_points[index[i][1]], src_points[index[i][2]]);
        src_triangle_list.push_back(src_triangle);
    }
}
// dst图的三角形
void ImageMorphing::setDstTriangleList() {
    for (int i = 0; i < index.size(); i++) {
        Triangle dst_triangle(dst_points[index[i][0]], dst_points[index[i][1]], dst_points[index[i][2]]);
        dst_triangle_list.push_back(dst_triangle);
    }
}
// morphing过程中每一帧的三角形
void ImageMorphing::setMidTriangleList() {
    for (int i = 0; i < frame_cnt; i++) {
        vector<Point> temp_mid_points;
        for (int j = 0; j < src_points.size(); j++) {
            float mid_x = float(src_points[j].x) + float(i+1)/(frame_cnt+1) * (dst_points[j].x - src_points[j].x);
            float mid_y = float(src_points[j].y) + float(i+1)/(frame_cnt+1) * (dst_points[j].y - src_points[j].y);
            Point mid_point(mid_x, mid_y);
            temp_mid_points.push_back(mid_point);
        }
        mid_points.push_back(temp_mid_points);
    }

    for (int i = 0; i < frame_cnt; i++) {
        vector<Triangle> temp_mid_triangles;
        for (int j = 0; j < index.size(); j++) {
            Triangle mid_triangle(mid_points[i][index[j][0]], mid_points[i][index[j][1]], mid_points[i][index[j][2]]);
            temp_mid_triangles.push_back(mid_triangle);
        }
        mid_triangle_list.push_back(temp_mid_triangles);
    }
}
// 重心法判断一个点是否在三角形内
bool ImageMorphing::isInTriangle(Point P, Triangle tri) {
    float x0 = tri.p3.x - tri.p1.x, y0 = tri.p3.y - tri.p1.y;
    float x1 = tri.p2.x - tri.p1.x, y1 = tri.p2.y - tri.p1.y;
    float x2 = P.x - tri.p1.x, y2 = P.y - tri.p1.y;

    float temp_00 = x0 * x0 + y0 * y0;
    float temp_01 = x0 * x1 + y0 * y1;
    float temp_02 = x0 * x2 + y0 * y2;
    float temp_11 = x1 * x1 + y1 * y1;
    float temp_12 = x1 * x2 + y1 * y2;

    float u = float(temp_11 * temp_02 - temp_01 * temp_12) / (float)(temp_00 * temp_11 - temp_01 * temp_01);
    float v = float(temp_00 * temp_12 - temp_01 * temp_02) / (float)(temp_00 * temp_11 - temp_01 * temp_01);
    if (u + v <= 1 && u >= 0 && v >= 0) return true;
    return false;
}
// 计算变换矩阵
CImg<float> ImageMorphing::computeTransformMatrix(Triangle before, Triangle after) {
    CImg<float> A(3, 3, 1, 1, 1);
    CImg<float> y1(1, 3, 1, 1, 0), y2(1, 3, 1, 1, 0);
    CImg<float> c1(1, 3, 1, 1, 0), c2(1, 3, 1, 1, 0);

    A(0, 0) = before.p1.x; A(1, 0) = before.p1.y;
    A(0, 1) = before.p2.x; A(1, 1) = before.p2.y;
    A(0, 2) = before.p3.x; A(1, 2) = before.p3.y;

    y1(0, 0) = after.p1.x; y2(0, 0) = after.p1.y;
    y1(0, 1) = after.p2.x; y2(0, 1) = after.p2.y;
    y1(0, 2) = after.p3.x; y2(0, 2) = after.p3.y;

    c1 = y1.solve(A);
    c2 = y2.solve(A);

    CImg<float> transform(3, 3, 1, 1, 0);
    for (int i = 0; i < 3; i++) {
        transform(i, 0) = c1(0, i);
        transform(i, 1) = c2(0, i);
    }
    transform(2, 2) = 1;
    return transform;
}
// 对每一帧进行morphing
CImgList<float> ImageMorphing::morphing() {
    int size = mid_triangle_list[0].size();

    result.push_back(src); // 放入第一帧
    for (int k = 0; k < frame_cnt; k++) {
        CImg<float> middle(dst._width, dst._height, 1, 3, 1);
        cimg_forXY(middle, i, j) {
            CImg<float> x(1, 3, 1, 1, 1), y1(1, 3, 1, 1, 1), y2(1, 3, 1, 1, 1);
            for (int m = 0; m < size; m++) {
                Point p(i, j);
                if (isInTriangle(p, mid_triangle_list[k][m])) {
                    x(0, 0) = i;
                    x(0, 1) = j;
                    // middle image到src image的变换
                    CImg<float> transform1 = computeTransformMatrix(mid_triangle_list[k][m], src_triangle_list[m]);
                    y1 = transform1 * x;
                    // middle image到dst image的变换
                    CImg<float> transform2 = computeTransformMatrix(mid_triangle_list[k][m], dst_triangle_list[m]);
                    y2 = transform2 * x;
                    // src和dst组合得到middle
                    float a = float(k+1)/(frame_cnt+1);
                    middle(i, j, 0) = (1 - a) * src(y1(0, 0), y1(0, 1), 0) + a * dst(y2(0, 0), y2(0, 1), 0);
                    middle(i, j, 1) = (1 - a) * src(y1(0, 0), y1(0, 1), 1) + a * dst(y2(0, 0), y2(0, 1), 1);
                    middle(i, j, 2) = (1 - a) * src(y1(0, 0), y1(0, 1), 2) + a * dst(y2(0, 0), y2(0, 1), 2);
                    break;
                }
            }
        }
        result.push_back(middle); // 放入中间morphing的每一帧
    }
    result.push_back(dst); // 放入最后一帧
    return result;
}

main.cpp文件:

#include "ImageMorphing.cpp"
#include <fstream>
#include <sstream>
#include <string>
int main() {
    CImg<float> src, dst;
    src.load_bmp("dataset/1.bmp");
    dst.load_bmp("dataset/2.bmp");

    // read source points
    vector<Point> src_points;
    ifstream infile_src_points;
    infile_src_points.open("points/src_points.txt");
    if (infile_src_points.is_open()) {
        string s;
        int x, y;
        while (getline(infile_src_points, s)) {
            stringstream ss(s);
            ss >> x >> y;
            Point p(x, y);
            src_points.push_back(p);
        }
    }
    else {
        cout << "Can't open the file src_points.txt" << endl;
        return 0;
    }

    // read destination points
    vector<Point> dst_points;
    ifstream infile_dst_points;
    infile_dst_points.open("points/dst_points.txt");
    if (infile_dst_points.is_open()) {
        string s;
        int x, y;
        while (getline(infile_dst_points, s)) {
            stringstream ss(s);
            ss >> x >> y;
            Point p(x, y);
            dst_points.push_back(p);
        }
    }
    else {
        cout << "Can't open the file dst_points.txt" << endl;
        return 0;
    }

    // read the indexs of triangles points
    vector<vector<int> > index;
    ifstream infile_tri;
    infile_tri.open("points/triangle.txt");
    if (infile_tri.is_open()) {
        string s;
        int index1, index2, index3;
        while (getline(infile_tri, s)) {
            vector<int> tempindex;
            stringstream ss(s);
            ss >> index1 >> index2 >> index3;
            tempindex.push_back(index1 - 1);
            tempindex.push_back(index2 - 1);
            tempindex.push_back(index3 - 1);
            index.push_back(tempindex);
        }
    }
    else {
        cout << "Can't open the file triangle.txt" << endl;
        return 0;
    }

    ImageMorphing imageMorphing(src, dst, src_points, dst_points, index, 11); // 11帧
    CImgList<float> result = imageMorphing.morphing();
    string dir_name;
    for (int i = 0; i < result.size(); i++) {
        dir_name = "result/";
        string s = to_string(i+1);
        s += ".bmp";
        dir_name += s;
        result[i].save_bmp(dir_name.c_str()); // Save every frame
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sinat_31790817/article/details/80278715