Java OpenCV桌面绘制轮廓
我在探测一种特定的物体方面取得了一些进展。实际上是一张卡片,就像你钱包里的其他卡片一样 现在,我被困在桌面上的照片。见: 蓝色(圆角)矩形表示检测到的轮廓。 紫色旋转矩形表示从检测到的轮廓中提取的Java OpenCV桌面绘制轮廓,java,opencv,Java,Opencv,我在探测一种特定的物体方面取得了一些进展。实际上是一张卡片,就像你钱包里的其他卡片一样 现在,我被困在桌面上的照片。见: 蓝色(圆角)矩形表示检测到的轮廓。 紫色旋转矩形表示从检测到的轮廓中提取的RotatedRect。 绿线只是边界框 我不需要这两个矩形。两个矩形都有90度角。这并不能让我得到正确的观点 我的问题: 如何从轮廓中获得尽可能精确的所有四边形角点?我创建了一个类四边形,该类创建了4个最大连接多边形顶点的四边形,这些顶点将在某个点相互相交。这几乎在任何情况下都有效 如果使用此代码,
RotatedRect
。
绿线只是边界框
我不需要这两个矩形。两个矩形都有90度角。这并不能让我得到正确的观点
我的问题:
如何从轮廓中获得尽可能精确的所有四边形角点?我创建了一个类
四边形
,该类创建了4个最大连接多边形顶点的四边形,这些顶点将在某个点相互相交。这几乎在任何情况下都有效
如果使用此代码,请记住在Quadrangle.warp
中调整宽度和高度。请注意,它不是100%完成的,例如,如果第一个和最后一个多边形顶点可能是连接的,则它们不会连接
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
class Line {
public Point offset;
public double angle;
public Line(Point offset, double angle) {
this.offset = offset.clone();
this.angle = angle;
}
public Point get(int length) {
Point result = offset.clone();
result.x += Math.cos(angle) * length;
result.y += Math.sin(angle) * length;
return result;
}
public Point getStart() {
return get(-5000);
}
public Point getEnd() {
return get(5000);
}
public void scale(double factor) {
offset.x *= factor;
offset.y *= factor;
}
public static Point intersect(Line l1, Line l2) {
return getLineLineIntersection(l1.getStart().x, l1.getStart().y, l1.getEnd().x, l1.getEnd().y,
l2.getStart().x, l2.getStart().y, l2.getEnd().x, l2.getEnd().y
);
}
public static Point getLineLineIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
double det1And2 = det(x1, y1, x2, y2);
double det3And4 = det(x3, y3, x4, y4);
double x1LessX2 = x1 - x2;
double y1LessY2 = y1 - y2;
double x3LessX4 = x3 - x4;
double y3LessY4 = y3 - y4;
double det1Less2And3Less4 = det(x1LessX2, y1LessY2, x3LessX4, y3LessY4);
if (det1Less2And3Less4 == 0){
// the denominator is zero so the lines are parallel and there's either no solution (or multiple solutions if the lines overlap) so return null.
return null;
}
double x = (det(det1And2, x1LessX2,
det3And4, x3LessX4) /
det1Less2And3Less4);
double y = (det(det1And2, y1LessY2,
det3And4, y3LessY4) /
det1Less2And3Less4);
return new Point(x, y);
}
protected static double det(double a, double b, double c, double d) {
return a * d - b * c;
}
}
class LineSegment extends Line implements Comparable {
public double length;
public LineSegment(Point offset, double angle, double length) {
super(offset, angle);
this.length = length;
}
public void melt(LineSegment segment) {
Point point = new Point();
point.x += Math.cos(angle) * length;
point.y += Math.sin(angle) * length;
point.x += Math.cos(segment.angle) * segment.length;
point.y += Math.sin(segment.angle) * segment.length;
angle = Math.atan2(point.y, point.x);
offset.x = (offset.x * length + segment.offset.x * segment.length) / (length + segment.length);
offset.y = (offset.y * length + segment.offset.y * segment.length) / (length + segment.length);
length += segment.length;
}
@Override
public int compareTo(Object other) throws ClassCastException {
if (!(other instanceof LineSegment)) {
throw new ClassCastException("A LineSegment object expected.");
}
return (int) (((LineSegment) other).length - this.length);
}
}
class Quadrangle {
static int
TOP = 0,
RIGHT = 1,
BOTTOM = 2,
LEFT = 3;
public Line[] lines = new Line[4];
public Quadrangle() {
}
private static double getAngle(Point p1, Point p2) {
return Math.atan2(p2.y - p1.y, p2.x - p1.x);
}
private static double getLength(Point p1, Point p2) {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
private static double roundAngle(double angle) {
return angle - (2*Math.PI) * Math.round(angle / (2 * Math.PI));
}
public static Quadrangle fromContour(MatOfPoint contour) {
List<Point> points = contour.toList();
List<LineSegment> segments = new ArrayList<>();
// Create line segments
for (int i = 0; i < points.size(); i++) {
double a = getAngle(points.get(i), points.get((i + 1) % points.size()));
double l = getLength(points.get(i), points.get((i + 1) % points.size()));
segments.add(new LineSegment(points.get(i), a, l));
}
// Connect line segments
double angleDiffMax = 2 * Math.PI / 100;
List<LineSegment> output = new ArrayList<>();
for (LineSegment segment : segments) {
if (output.isEmpty()) {
output.add(segment);
} else {
LineSegment top = output.get(output.size() - 1);
double d = roundAngle(segment.angle - top.angle);
if (Math.abs(d) < angleDiffMax) {
top.melt(segment);
} else {
output.add(segment);
}
}
}
Collections.sort(output);
Quadrangle quad = new Quadrangle();
for (int o = 0; o < 4; o += 1) {
for (int i = 0; i < 4; i++) {
if (Math.abs(roundAngle(output.get(i).angle - (2 * Math.PI * o / 4))) < Math.PI / 4) {
quad.lines[o] = output.get(i);
}
}
}
return quad;
}
public void scale(double factor) {
for (int i = 0; i < 4; i++) {
lines[i].scale(factor);
}
}
public Mat warp(Mat src) {
Mat result = src.clone();
Core.line(result, lines[TOP].get(-5000), lines[TOP].get(5000), new Scalar(200, 100, 100), 8);
Core.line(result, lines[RIGHT].get(-5000), lines[RIGHT].get(5000), new Scalar(0, 255, 0), 8);
Core.line(result, lines[BOTTOM].get(-5000), lines[BOTTOM].get(5000), new Scalar(255, 0, 0), 8);
Core.line(result, lines[LEFT].get(-5000), lines[LEFT].get(5000), new Scalar(0, 0, 255), 8);
Point p = Line.intersect(lines[TOP], lines[LEFT]);
System.out.println(p);
if (p != null) {
Core.circle(result, p, 30, new Scalar(0, 0, 255), 8);
}
double width = 1400;
double height = width / 2.15;
Point[] srcProjection = new Point[4], dstProjection = new Point[4];
srcProjection[0] = Line.intersect(lines[TOP], lines[LEFT]);
srcProjection[1] = Line.intersect(lines[TOP], lines[RIGHT]);
srcProjection[2] = Line.intersect(lines[BOTTOM], lines[LEFT]);
srcProjection[3] = Line.intersect(lines[BOTTOM], lines[RIGHT]);
dstProjection[0] = new Point(0, 0);
dstProjection[1] = new Point(width - 1, 0);
dstProjection[2] = new Point(0, height - 1);
dstProjection[3] = new Point(width - 1, height - 1);
Mat warp = Imgproc.getPerspectiveTransform(new MatOfPoint2f(srcProjection), new MatOfPoint2f(dstProjection));
Mat rotated = new Mat();
Size size = new Size(width, height);
Imgproc.warpPerspective(src, rotated, warp, size, Imgproc.INTER_LINEAR);
return rotated;
}
}
import java.util.ArrayList;
导入java.util.Collections;
导入java.util.List;
导入org.opencv.core.*;
导入org.opencv.imgproc.imgproc;
班级线{
公共点偏移;
公共双角度;
公共线(点偏移,双角度){
this.offset=offset.clone();
这个角度=角度;
}
公共点获取(整数长度){
点结果=offset.clone();
结果x+=数学cos(角度)*长度;
结果.y+=数学正弦(角度)*长度;
返回结果;
}
公共点getStart(){
返回get(-5000);
}
公共点getEnd(){
返回获取(5000);
}
公共空间比例(双因素){
偏移量x*=系数;
偏移量y*=系数;
}
公共静态点相交(l1线、l2线){
返回GetLineIntersection(l1.getStart().x,l1.getStart().y,l1.getEnd().x,l1.getEnd().y,
l2.getStart().x,l2.getStart().y,l2.getEnd().x,l2.getEnd().y
);
}
公共静态点GetLineIntersection(双x1、双y1、双x2、双y2、双x3、双y3、双x4、双y4){
双Det1和2=det(x1,y1,x2,y2);
双Det3和4=det(x3,y3,x4,y4);
双x1LessX2=x1-x2;
双y1LessY2=y1-y2;
双x3LessX4=x3-x4;
双y3LessY4=y3-y4;
双Det1Less2和3Less4=det(x1LessX2、y1LessY2、x3LessX4、y3LessY4);
如果(Det1Less2和3Less4==0){
//分母为零,因此直线平行,并且没有解(如果直线重叠,则有多个解),因此返回null。
返回null;
}
双x=(det(DET1和DET2,x1LessX2,
详图3和4,x3LessX4)/
无细节2,无细节4);
双y=(det(det1And2,y1LessY2,
第3条和第4条,y3LessY4)/
无细节2,无细节4);
返回新点(x,y);
}
受保护静态双检测器(双a、双b、双c、双d){
返回a*d-b*c;
}
}
类LineSegment扩展了直线段{
公共双倍长度;
公共线段(点偏移、双角度、双长度){
super(偏移、角度);
这个长度=长度;
}
公共空心融化(线段){
点=新点();
点x+=数学cos(角度)*长度;
点y+=数学正弦(角度)*长度;
点x+=数学cos(段角度)*段长度;
点y+=数学sin(段角度)*段长度;
角度=数学atan2(点y,点x);
offset.x=(offset.x*长度+段.offset.x*段.长度)/(长度+段.长度);
offset.y=(offset.y*长度+段.offset.y*段.长度)/(长度+段.长度);
长度+=段长度;
}
@凌驾
public int compareTo(对象其他)抛出ClassCastException{
如果(!(线段的其他实例)){
抛出新的ClassCastException(“应为线段对象”);
}
返回(int)(((线段)其他).length-此.length);
}
}
阶级四合院{
静态整数
TOP=0,
右=1,
底部=2,
左=3;
公共行[]行=新行[4];
公共四合院(){
}
专用静态双getAngle(点p1、点p2){
返回Math.atan2(p2.y-p1.y,p2.x-p1.x);
}
专用静态双getLength(点p1、点p2){
返回Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2));
}
专用静态双圆角(双角度){
返回角-(2*Math.PI)*数学圆(角度/(2*Math.PI));
}
公共静态四边形fromContour(MatOfPoint contour){
列表点=contour.toList();
列表段=新的ArrayList();
//创建线段
对于(int i=0;i