gpt4 book ai didi

ios - 检测带有圆角的卡片边缘

转载 作者:技术小花猫 更新时间:2023-10-29 10:34:32 28 4
gpt4 key购买 nike

您好,我目前正在开发一个 OCR 阅读应用程序,我已经成功地使用 AVFoundation 框架捕获了卡片图像。

下一步,我需要找出卡片的边缘,以便我可以从主要捕获图像中裁剪卡片图像,然后将其发送到 OCR 引擎进行处理。

现在的主要问题是找到卡片的边缘,我正在使用下面的代码(取自另一个开源项目),它为此目的使用 OpenCV。如果卡片是纯矩形卡片或纸,它工作正常。但是当我使用带有圆角的卡(例如驾驶执照)时,它无法检测到。另外我在 OpenCV 方面没有太多专业知识,任何人都可以帮助我解决这个问题吗?

- (void)detectEdges
{
cv::Mat original = [MAOpenCV cvMatFromUIImage:_adjustedImage];
CGSize targetSize = _sourceImageView.contentSize;
cv::resize(original, original, cvSize(targetSize.width, targetSize.height));

cv::vector<cv::vector<cv::Point>>squares;
cv::vector<cv::Point> largest_square;

find_squares(original, squares);
find_largest_square(squares, largest_square);

if (largest_square.size() == 4)
{

// Manually sorting points, needs major improvement. Sorry.

NSMutableArray *points = [NSMutableArray array];
NSMutableDictionary *sortedPoints = [NSMutableDictionary dictionary];

for (int i = 0; i < 4; i++)
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithCGPoint:CGPointMake(largest_square[i].x, largest_square[i].y)], @"point" , [NSNumber numberWithInt:(largest_square[i].x + largest_square[i].y)], @"value", nil];
[points addObject:dict];
}

int min = [[points valueForKeyPath:@"@min.value"] intValue];
int max = [[points valueForKeyPath:@"@max.value"] intValue];

int minIndex;
int maxIndex;

int missingIndexOne;
int missingIndexTwo;

for (int i = 0; i < 4; i++)
{
NSDictionary *dict = [points objectAtIndex:i];

if ([[dict objectForKey:@"value"] intValue] == min)
{
[sortedPoints setObject:[dict objectForKey:@"point"] forKey:@"0"];
minIndex = i;
continue;
}

if ([[dict objectForKey:@"value"] intValue] == max)
{
[sortedPoints setObject:[dict objectForKey:@"point"] forKey:@"2"];
maxIndex = i;
continue;
}

NSLog(@"MSSSING %i", i);

missingIndexOne = i;
}

for (int i = 0; i < 4; i++)
{
if (missingIndexOne != i && minIndex != i && maxIndex != i)
{
missingIndexTwo = i;
}
}


if (largest_square[missingIndexOne].x < largest_square[missingIndexTwo].x)
{
//2nd Point Found
[sortedPoints setObject:[[points objectAtIndex:missingIndexOne] objectForKey:@"point"] forKey:@"3"];
[sortedPoints setObject:[[points objectAtIndex:missingIndexTwo] objectForKey:@"point"] forKey:@"1"];
}
else
{
//4rd Point Found
[sortedPoints setObject:[[points objectAtIndex:missingIndexOne] objectForKey:@"point"] forKey:@"1"];
[sortedPoints setObject:[[points objectAtIndex:missingIndexTwo] objectForKey:@"point"] forKey:@"3"];
}


[_adjustRect topLeftCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:@"0"] CGPointValue]];
[_adjustRect topRightCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:@"1"] CGPointValue]];
[_adjustRect bottomRightCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:@"2"] CGPointValue]];
[_adjustRect bottomLeftCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:@"3"] CGPointValue]];
}

original.release();


}

最佳答案

这种朴素的实现是基于 squares.cpp 中展示的一些技术。 ,在 OpenCV 示例目录中可用。以下帖子也讨论了类似的应用:

@John,下面的代码已经用你提供的示例图像和我创建的另一个图像进行了测试:

处理管道以 findSquares() 开始,这是对 OpenCV 的 squares.cpp 演示实现的相同功能的简化。此函数将输入图像转换为灰度并应用模糊以改进边缘检测 (Canny):

边缘检测很好,但是需要形态学操作(膨胀)来连接附近的线:

之后,我们尝试找到轮廓(边缘)并从中组装正方形。如果我们尝试在输入图像上绘制所有检测到的正方形,结果将是:

它看起来不错,但由于检测到的方 block 太多,这并不是我们要找的。然而,最大的正方形实际上是卡片,所以从这里开始就很简单了,我们只需找出最大的正方形即可。这正是 findLargestSquare() 所做的。

一旦我们知道了最大的正方形,我们就可以简单地在正方形的角上画红点以进行调试:

如您所见,检测并不完美,但似乎足够好用于大多数用途。这不是一个可靠的解决方案,我只想分享一种解决问题的方法。我相信还有其他方法可以解决这个问题,您可能会更感兴趣。祝你好运!

#include <iostream>
#include <cmath>
#include <vector>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/imgproc_c.h>

/* angle: finds a cosine of angle between vectors, from pt0->pt1 and from pt0->pt2
*/
double angle(cv::Point pt1, cv::Point pt2, cv::Point pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

/* findSquares: returns sequence of squares detected on the image
*/
void findSquares(const cv::Mat& src, std::vector<std::vector<cv::Point> >& squares)
{
cv::Mat src_gray;
cv::cvtColor(src, src_gray, cv::COLOR_BGR2GRAY);

// Blur helps to decrease the amount of detected edges
cv::Mat filtered;
cv::blur(src_gray, filtered, cv::Size(3, 3));
cv::imwrite("out_blur.jpg", filtered);

// Detect edges
cv::Mat edges;
int thresh = 128;
cv::Canny(filtered, edges, thresh, thresh*2, 3);
cv::imwrite("out_edges.jpg", edges);

// Dilate helps to connect nearby line segments
cv::Mat dilated_edges;
cv::dilate(edges, dilated_edges, cv::Mat(), cv::Point(-1, -1), 2, 1, 1); // default 3x3 kernel
cv::imwrite("out_dilated.jpg", dilated_edges);

// Find contours and store them in a list
std::vector<std::vector<cv::Point> > contours;
cv::findContours(dilated_edges, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

// Test contours and assemble squares out of them
std::vector<cv::Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional to the contour perimeter
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);

// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 && std::fabs(contourArea(cv::Mat(approx))) > 1000 &&
cv::isContourConvex(cv::Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = std::fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}

if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}

/* findLargestSquare: find the largest square within a set of squares
*/
void findLargestSquare(const std::vector<std::vector<cv::Point> >& squares,
std::vector<cv::Point>& biggest_square)
{
if (!squares.size())
{
std::cout << "findLargestSquare !!! No squares detect, nothing to do." << std::endl;
return;
}

int max_width = 0;
int max_height = 0;
int max_square_idx = 0;
for (size_t i = 0; i < squares.size(); i++)
{
// Convert a set of 4 unordered Points into a meaningful cv::Rect structure.
cv::Rect rectangle = cv::boundingRect(cv::Mat(squares[i]));

//std::cout << "find_largest_square: #" << i << " rectangle x:" << rectangle.x << " y:" << rectangle.y << " " << rectangle.width << "x" << rectangle.height << endl;

// Store the index position of the biggest square found
if ((rectangle.width >= max_width) && (rectangle.height >= max_height))
{
max_width = rectangle.width;
max_height = rectangle.height;
max_square_idx = i;
}
}

biggest_square = squares[max_square_idx];
}

int main()
{
cv::Mat src = cv::imread("cc.png");
if (src.empty())
{
std::cout << "!!! Failed to open image" << std::endl;
return -1;
}

std::vector<std::vector<cv::Point> > squares;
findSquares(src, squares);

// Draw all detected squares
cv::Mat src_squares = src.clone();
for (size_t i = 0; i < squares.size(); i++)
{
const cv::Point* p = &squares[i][0];
int n = (int)squares[i].size();
cv::polylines(src_squares, &p, &n, 1, true, cv::Scalar(0, 255, 0), 2, CV_AA);
}
cv::imwrite("out_squares.jpg", src_squares);
cv::imshow("Squares", src_squares);

std::vector<cv::Point> largest_square;
findLargestSquare(squares, largest_square);

// Draw circles at the corners
for (size_t i = 0; i < largest_square.size(); i++ )
cv::circle(src, largest_square[i], 4, cv::Scalar(0, 0, 255), cv::FILLED);
cv::imwrite("out_corners.jpg", src);

cv::imshow("Corners", src);
cv::waitKey(0);

return 0;
}

关于ios - 检测带有圆角的卡片边缘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17338488/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com