大家是否听过一个极其困难的问题——图像信息区的提取与校正?对这个问题,您又是如何理解其难度的呢?

图像信息区提取与校正:从闭合边界到四边形顶点,解密难题攻略(图1)

图像信息区提取与校正:从闭合边界到四边形顶点,解密难题攻略(图2)

关于这个问题,实际上并不轻松。一开始,我便遇到了一个棘手的麻烦——如何确保信息区域的边界能够做到闭合。即便是在这个阶段,亦未能发掘出我们所期望获取的信息内容。

Mat src = imread("1.png"

);

imshow("src img"

, src);

Mat source = src.clone

();

Mat bkup = src.clone

();

Mat img = src.clone

();

cvtColor(img, img, CV_RGB2GRAY); //二值化 imshow("gray"

, img);

//equalizeHist(img, img); //imshow("equal", img); GaussianBlur(img, img, Size(5, 5), 0, 0); //高斯滤波 //获取自定义核 Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的 //膨胀操作 dilate(img, img, element); //实现过程中发现,适当的膨胀很重要 imshow("dilate"

, img);

Canny(img, img, 30, 120, 3); //边缘提取 imshow("get contour"

, img);

}

图像信息区提取与校正:从闭合边界到四边形顶点,解密难题攻略(图3)

图像信息区提取与校正:从闭合边界到四边形顶点,解密难题攻略(图4)

然而,并未因此而丧气。我成功找到一种简洁有效的方式用于挑选轮廓,即寻找图像中面积最大的边缘作为我们期望的信息区域。原因在于,对于证件或文件类的扫描照片,其主要部分常占据整个图像较大的比例。

vector<vector

> contours;

vector<vector

> f_contours;

std::vector

approx2;

//注意第5个参数为CV_RETR_EXTERNAL,只检索外框 findContours(img, f_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓 //求出面积最大的轮廓 int

max_area = 0;

int

index;

for (int

i = 0; i < f_contours.size(); i++)

{

double tmparea = fabs

(contourArea(f_contours[i]));

if

(tmparea > max_area)

{

index = i;

max_area = tmparea;

}

}

contours.push_back(f_contours[index]);

接着,新的问题出现了,如何根据四边形的点集找出初始四边形的四个顶点?这确实并非易事!

图像信息区提取与校正:从闭合边界到四边形顶点,解密难题攻略(图5)

起初我曾尝试过只通过四边形的点集找出其中的四个定点。例如,可以选择x坐标最大或最小的点分别作为右上角和左下角的顶点。然而,此方法存在很大局限性,其应用受到四边形形态等因素影响较大。

若诸位对此有所见解,欢迎不吝赐教。我十分渴望能共同探寻解决之道王者荣耀正版卖挂网站,解决这个让我苦恼已久的问题!

图像信息区提取与校正:从闭合边界到四边形顶点,解密难题攻略(图6)

尽管暂无人能给出解答,我们仍需继续深入研究。我决定采用霍夫变换来提取四边形的边线王者绘制透视,通过求取两条直线的交点以获得四边形的顶点位置。然而,此处同样存在一个难题,如何保证所分析得出的交点恰好构成所需的四边形?

cv::Point2f computeIntersect(cv::Vec4i a, cv::Vec4i b)

{

int

x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3];

int

x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];

if (float d = ((float

)(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;

}

else return

cv::Point2f(-1, -1);

}

为此,我们不得不重复调整霍夫变换的参数,进行反复迭代,直到找到满足要求的直线情形。之后,还要进一步筛选这些交点,确定四个顶点的具体位置。可见,这个过程并不轻松!

bool IsGoodPoints = true

;

//保证点与点的距离足够大以排除错误点for (int

i = 0; i < corners.size(); i++)

{

for (int

j = i + 1; j < corners.size(); j++)

{

int distance = sqrt

((corners[i].x - corners[j].x)*(corners[i].x - corners[j].x) + (corners[i].y - corners[j].y)*(corners[i].y - corners[j].y));

if

(distance < 5)

{

IsGoodPoints = false

;

}

}

}

if (!IsGoodPoints) continue

;

最后,我们成功得到了原始图片信息区域中的四个顶点。但请注意王者绘制透视,为完成映射变换,我们尚需处理转化后图像的四个顶点。在此过程中,涉及到许多细致入微之处。有无朋友曾经碰到过此类困境?期待您在评论区留下您的观点,与我展开讨论。同时,不要忘记给我点赞并分享哦!

cv::approxPolyDP(cv::Mat(corners), approx, cv::arcLength(cv::Mat(corners), true) * 0.02, true);if (lines.size() == 4 && corners.size() == 4 && approx.size() == 4)

{

flag = 1; break;

}

bool x_sort(const Point2f & m1, const

Point2f & m2)

{

return

m1.x < m2.x;

}

//确定四个点的中心线void sortCorners(std::vector

& corners,

cv::Point2f center)

{

std::vector

top, bot;

vector

backup = corners;

sort(corners, x_sort); //注意先按x的大小给4个点排序 for (int

i = 0; i < corners.size(); i++)

{

if (corners[i].y < center.y && top.size() < 2) //这里的小于2是为了避免三个顶点都在top的情况

top.push_back(corners[i]);

else

bot.push_back(corners[i]);

}

corners.clear();

if

(top.size() == 2 && bot.size() == 2)

{

//cout << "log" << endl;

cv::Point2f tl = top[0].x > top[1].x ? top[1] : top[0];

cv::Point2f tr = top[0].x > top[1].x ? top[0] : top[1];

cv::Point2f bl = bot[0].x > bot[1].x ? bot[1] : bot[0];

cv::Point2f br = bot[0].x > bot[1].x ? bot[0] : bot[1];

corners.push_back(tl);

corners.push_back(tr);

corners.push_back(br);

corners.push_back(bl);

}

else

{

corners = backup;

}

}