在找到最大轮廓(在这种情况下是优惠券)后,应用warpPerspective导致不一致的扭曲,它会在扭曲时向左旋转一些图像,但对于某些输入帧效果很好。
输入完美翘曲的输入图像
输出完美翘曲
输入翘曲声音的输入图像
private Mat processMatToFindLargestContourAndApplyWarp(Mat srcMat) {
Mat processedMat = new Mat();
Imgproc.cvtColor(srcMat, processedMat, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(processedMat, processedMat, new Size(5, 5), 5);
Imgproc.threshold(processedMat, processedMat, 127, 255, Imgproc.THRESH_BINARY);
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(processedMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
double maxVal = 0;
int maxValIdx = 0;
for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) {
double contourArea = Imgproc.contourArea(contours.get(contourIdx));
if (maxVal < contourArea) {
maxVal = contourArea;
maxValIdx = contourIdx;
}
}
if (!contours.isEmpty()) {
Imgproc.drawContours(processedMat, contours, maxValIdx, new Scalar(0, 255, 0), 3);
return warp(srcMat, contours.get(maxValIdx));
} else {
Toast.makeText(this, "Error: Token contour not found", Toast.LENGTH_LONG).show();
}
return srcMat;
}
public Mat warp(Mat inputMat, MatOfPoint selectedContour) {
Mat outputMat;
try {
MatOfPoint2f new_mat = new MatOfPoint2f(selectedContour.toArray());
MatOfPoint2f approxCurve_temp = new MatOfPoint2f();
int contourSize = (int) selectedContour.total();
Imgproc.approxPolyDP(new_mat, approxCurve_temp, contourSize * 0.05, true);
double[] temp_double;
temp_double = approxCurve_temp.get(0,0);
Point p1 = new Point(temp_double[0], temp_double[1]);
temp_double = approxCurve_temp.get(1,0);
Point p3 = new Point(temp_double[0], temp_double[1]);
temp_double = approxCurve_temp.get(2,0);
Point p4 = new Point(temp_double[0], temp_double[1]);
temp_double = approxCurve_temp.get(3,0);
Point p2 = new Point(temp_double[0], temp_double[1]);
List<Point> source = new ArrayList<Point>();
source.add(p1);
source.add(p2);
source.add(p3);
source.add(p4);
Log.e("inPoints", "" + source);
Mat startM = Converters.vector_Point2f_to_Mat(source);
int resultWidth = 846;
int resultHeight = 2048;
outputMat = new Mat(resultWidth, resultHeight, CvType.CV_8UC4);
Point ocvPOut1 = new Point(0, 0);
Point ocvPOut2 = new Point(resultWidth, 0);
Point ocvPOut3 = new Point(0, resultHeight);
Point ocvPOut4 = new Point(resultWidth, resultHeight);
List<Point> dest = new ArrayList<Point>();
dest.add(ocvPOut1);
dest.add(ocvPOut2);
dest.add(ocvPOut3);
dest.add(ocvPOut4);
Mat endM = Converters.vector_Point2f_to_Mat(dest);
Mat perspectiveTransform = Imgproc.getPerspectiveTransform(startM, endM);
Imgproc.warpPerspective(inputMat, outputMat, perspectiveTransform, new Size(resultWidth, resultHeight));
} catch (Exception e) {
return null;
}
return outputMat;
}
我尝试过切换边缘点,但是它适用于第二张图像而不适用于第一张图像。我是否必须检查并按顺序安排积分,如果是这样,任何人都可以指导我如何,或者有更好的方法来处理这个问题?
在我的日志结果中,Imgproc.approxPolyDP收集的输入源点对于具有优惠券的不同图像的排列不一致,即使是微小的角度。
答案 0 :(得分:1)
我已经对这种情况进行了快速解决,我确信会有更好的方法来实现这一目标,但现在可以使用,修复方法是手动将轮廓点顺序排列到顺时针方向(即TopLeft)无论输入点排列如何,每次都是TopRight,BottomRight,BottomLeft。
如果有人遇到我的情况,这是代码
private Point[] orderCorners(Point[] cornersUnordered) {
Point[] cornerPoints = new Point[4];
Point p1, p2, p3, p4;
Point topLeft = null, topRight = null, botRight = null, botLeft = null;
List<Point> corners = new ArrayList<Point>();
for (int i=0; i < cornersUnordered.length; ++i)
corners.add(cornersUnordered[i]);
/* Top set of points */
// find p1
p1 = corners.get(0);
for (Point point : corners) {
if (point.y < p1.y) {
p1 = point;
}
}
corners.remove(p1);
// find p2
p2 = corners.get(0);
for (Point point : corners) {
if (distance(p1, point) < distance(p1, p2)) {
p2 = point;
}
}
corners.remove(p2);
/* Identify top left and top right */
/*
* Note that the logic is safe if the points have equal x values. Safe
* in the sense that different points will get assigned to topLeft and
* topRight
*/
topLeft = p1.x < p2.x ? p1 : p2;
topRight = p2.x > p1.x ? p2 : p1;
/* Bottom set of points */
// corners only contains 2 points, the bottom ones
p3 = corners.get(0);
p4 = corners.get(1);
botRight = p3.x > p4.x ? p3 : p4;
botLeft = p4.x < p3.x ? p4 : p3;
cornerPoints[0] = topLeft;
cornerPoints[1] = topRight;
cornerPoints[2] = botRight;
cornerPoints[3] = botLeft;
return cornerPoints;
}
private double distance(Point p1, Point p2) {
return Math.sqrt(Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2));
}
这是上面修复的destinationPoints排列:
Point ocvPOut1 = new Point(0, 0);
Point ocvPOut2 = new Point(resultWidth, 0);
Point ocvPOut3 = new Point(resultWidth, resultHeight);
Point ocvPOut4 = new Point(0, resultHeight);
特别感谢@Alexander Reynolds