身体证检测与识别(二)——HED边缘检测与矫正

前言

1.关于边缘检测,我这里用了HED这个边缘检测网络,HED创作于2015年,骨干网络是state-of-the-art的VGG-16,并且使用迁移学习初始化了网络权重。关于HED的算法原理与训练模型代码可以转到github
2.OpenCV也有好几边缘检测算法可用,就拿最常用的Canny算法来说,对背景复杂一点的图像,手动调参往往是这个场景下边缘很完美的提取出来,当换个有差异的场景,干扰源不一样,要得到好一些的效果,参数阈值又得重新调一遍,如果要做一个能商用的项目,Canny算法是不能完美解决边缘提取的问题。

项目流程

1.先用HED提取身份证边缘。
2.用霍夫变换直线检测对边缘图像做直线检测。
3.对检测到的直线做排序与筛选,得到最终的边缘。
4.找到边缘线的相交点。
5.以四条线的四个相交点来矫正得到的最终身份证目标。

代码实现

1.使用用OpenCV的DNN来做模型推理,得到最终于的边缘图像。

int hedEdge(cv::dnn::Net& edge_net, cv::Mat& cv_book, cv::Mat& cv_hed)
{
	if (cv_book.empty())
	{
		return -2;
	}
	cv::Mat cv_gamma;

	cv::Size reso(512, 512);
	cv::Mat blob = cv::dnn::blobFromImage(cv_book, 1.0 / 255.0, reso, cv::Scalar(0, 0, 0), true, false);
	edge_net.setInput(blob);
	cv_hed = edge_net.forward();

	cv::resize(cv_hed.reshape(1, reso.height), cv_hed, cv_book.size(), cv::INTER_LINEAR);

	cv_hed.convertTo(cv_hed, CV_8UC1, 255);

	return 0;
}

得到边缘图像:
在这里插入图片描述
2.对得到的边缘图像进行直线检测并分出垂直与水平线。

struct Line
{
    cv::Point LP1;
    cv::Point LP2;
    cv::Point LC;

    Line(cv::Point p1, cv::Point p2)
    {
        LP1 = p1;
        LP2 = p2;
        LC = cv::Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
    }
};
void lineDetection(cv::Mat cv_edge,std::vector<Line> &h_lines, std::vector<Line>& v_lines)
{

	cv::Mat cv_dilate, cv_erode;
	cv::Mat element_e = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5), cv::Point(-1, -1));
	cv::Mat element_d = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));
	
	cv::erode(cv_edge, cv_erode, element_e);
	cv::dilate(cv_erode, cv_dilate, element_d);

    std::vector<cv::Vec4i> lines;
 
    HoughLinesP(cv_dilate, lines, 1, CV_PI / 180, 100,80, 8);
    for (size_t i = 0; i < lines.size(); i++) 
    {
        cv::Vec4i v = lines[i];
        double delta_x = v[0] - v[2], delta_y = v[1] - v[3];
        Line l(cv::Point(v[0], v[1]), cv::Point(v[2], v[3]));

        if (fabs(delta_x) > fabs(delta_y))
        {
            h_lines.push_back(l);
        }
        else
        {
            v_lines.push_back(l);
        }
    }
}

运行结果:
在这里插入图片描述
3.提取最终边缘线并得到四个矫正的点。

bool cmpLineY(const Line& p1, const Line& p2)
{
    return p1.LC.y < p2.LC.y;
}

bool cmpLineX(const Line& p1, const Line& p2)
{
    return p1.LC.x < p2.LC.x;
}


cv::Point2f computeIntersect(Line l1, Line l2) 
{
    int x1 = l1.LP1.x, x2 = l1.LP2.x, y1 = l1.LP1.y, y2 = l1.LP2.y;
    int x3 = l2.LP1.x, x4 = l2.LP2.x, y3 = l2.LP1.y, y4 = l2.LP2.y;
    if (float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
    {
        cv::Point2f pt;
        pt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
        pt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
        return pt;
    }
    return cv::Point2f(-1, -1);
}

void screeningLine(cv::Mat cv_src, std::vector<Line> &h_lines, std::vector<Line> &v_lines, std::vector<cv::Point> &out_points)
{
	if (h_lines.size() >= 2 && v_lines.size() >= 2)
	{
		std::sort(h_lines.begin(), h_lines.end(), cmpLineY);
		std::sort(v_lines.begin(), v_lines.end(), cmpLineX);
		out_points.push_back(computeIntersect(h_lines[0], v_lines[0]));
		out_points.push_back(computeIntersect(h_lines[0], v_lines[v_lines.size() - 1]));
		out_points.push_back(computeIntersect(h_lines[h_lines.size() - 1], v_lines[0]));
		out_points.push_back(computeIntersect(h_lines[h_lines.size() - 1], v_lines[v_lines.size() - 1]));
	}
	else
	{
		out_points.push_back(cv::Point2f(2, 2));
		out_points.push_back(cv::Point2f(2, cv_src.rows - 2));
		out_points.push_back(cv::Point2f(cv_src.cols - 2, 2));
		out_points.push_back(cv::Point2f(cv_src.cols - 2, cv_src.rows - 2));
	}
}

运行结果:
在这里插入图片描述

4.按四个点矫正图像。

int reviseImage(cv::Mat& cv_src, cv::Mat& cv_dst, std::vector<cv::Point>& in_points)
{
	cv::Point point_f, point_b;

	point_f.x = (in_points.at(0).x < in_points.at(2).x) ? in_points.at(0).x : in_points.at(2).x;
	point_f.y = (in_points.at(0).y < in_points.at(1).y) ? in_points.at(0).y : in_points.at(1).y;
	point_b.x = (in_points.at(3).x > in_points.at(1).x) ? in_points.at(3).x : in_points.at(1).x;
	point_b.y = (in_points.at(3).y > in_points.at(2).y) ? in_points.at(3).y : in_points.at(2).y;

	
	//代码取目标的最小外接矩形,但倾斜45度时会出现比例变形的现象
	cv::Rect rect(point_f, point_b);
	cv_dst = cv::Mat::zeros(rect.height, rect.width, CV_8UC3);

	std::vector<cv::Point2f> dst_pts;
	dst_pts.push_back(cv::Point2f(0, 0));
	dst_pts.push_back(cv::Point2f(rect.width - 1, 0));
	dst_pts.push_back(cv::Point2f(0, rect.height - 1));
	dst_pts.push_back(cv::Point2f(rect.width - 1, rect.height - 1));

	std::vector<cv::Point2f> tr_points;
	tr_points.push_back(in_points.at(0));
	tr_points.push_back(in_points.at(1));
	tr_points.push_back(in_points.at(2));
	tr_points.push_back(in_points.at(3));

	cv::Mat transmtx = getPerspectiveTransform(tr_points, dst_pts);

	warpPerspective(cv_src, cv_dst, transmtx, cv_dst.size());

	return 0;
}

运行结果:
在这里插入图片描述

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值