OpenCV形状颜色识别Java,Android

纯java代码实现安卓的CV识别图形形状颜色
半年前开的帖,一直在忙 回来更新下。
cv环境已经没有了,图就用以前的吧,代码我贴出来,凭记忆解释一下,算法实现的,我会说明逻辑
没啥描述
这是效果
首先,环境是OpenCV3.4.3,jdk1.8,安卓4.3,工具类是在java编辑和测试的,java稍改即可用,现在代码是从同学那拷贝过来的,已经用在了安卓环境,白色字体是反向绘制出来的,以下是代码 主要分三步,抓图预处理,形状判定,色彩判定。

先贴出简单封装的安卓调用CV工具类

import java.util.ArrayList;
import java.util.List;

import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;

/**
 * 重构 OpenCV Utils
 * 
 * @author PiaoZhenJia
 *
 */
public class OpenCvUtils {
	private static final String tag = "CVUT2";

	/**
	 * 得到图形中心点
	 */
	public Point getCenterPoint(Point[] points) {
		double centerX = 0.0;
		double centerY = 0.0;
		for (Point point : points) {
			centerX += point.x;
			centerY += point.y;
		}
		Point result = new Point();
		result.x = (int) (centerX / points.length);
		result.y = (int) (centerY / points.length);
		return result;
	}

	/**
	 * 获得角点 传入单调且不能是二值化的图像
	 */
	public Point[] findp(Mat mat) {
		final int maxCorners = 50, blockSize = 3;
		final double qualityLevel = 0.01, minDistance = 20.0, k = 0.04;
		final boolean userHarrisDetector = false;
		MatOfPoint corners = new MatOfPoint();// 省略了转化成灰度图像
		this.toGrayMat(mat);
		// 计算角点
		// image:8位或32位浮点型输入图像,单通道
		// corners:保存检测出的角点
		// maxCorners:角点数目最大值,如果实际检测的角点超过此值,则只返回前maxCorners个强角点
		// qualityLevel:角点的品质因子---------重要
		// minDistance:对于初选出的角点而言,如果在其周围minDistance范围内存在其他更强角点,则将此角点删除
		// mask:指定感兴趣区,如不需在整幅图上寻找角点,则用此参数指定ROI。也可以new Mat()来代替,这样就是没有mask.
		// blockSize:计算协方差矩阵时的窗口大小
		// useHarrisDetector:指示是否使用Harris角点检测,如不指定,则计算shi-tomasi角点
		// harrisK:Harris角点检测需要的k值
		Imgproc.goodFeaturesToTrack(mat, corners, maxCorners, qualityLevel,
				minDistance, new Mat(), blockSize, userHarrisDetector, k);
		Point[] pCorners = corners.toArray();
		return pCorners;
	}

	/**
	 * 画个圈圈祝福你
	 */
	public void drawCircleByPoint(Mat mat, Point point) {
		Imgproc.circle(mat, point, 2, new Scalar(255, 255, 0), 1);
	}

	/**
	 * 横向填充杂色
	 */
	public void coverBackGroundToBlack(Mat mat) {
		final double blackPixle[] = { 0.0 };
		for (int y = 0; y < mat.height(); y++) {
			for (int x = 0; x < mat.width(); x++) {
				double pixle[] = mat.get(y, x);
				if (pixle[0] == 255.0) {// 如果是白色
					mat.put(y, x, blackPixle);
				} else {// 遇到黑色
					break;
				}
			}
			for (int x = mat.width() - 1; x > 0; x--) {
				double pixle[] = mat.get(y, x);
				if (pixle[0] == 255.0) {// 如果是白色
					mat.put(y, x, blackPixle);
				} else {// 遇到黑色
					break;
				}
			}
		}
		Log.d(tag, "背景涂黑完成");
	}

	/**
	 * 从Bitmap得到Mat
	 */
	public Mat bitmapToMat(Bitmap bitmap) {
		Bitmap bit = bitmap.copy(Bitmap.Config.ARGB_8888, false);
		Mat src = new Mat(bit.getHeight(), bit.getWidth(), CvType.CV_8UC(3));
		Utils.bitmapToMat(bit, src);
		Log.d(tag, "Bitmap转换Mat完成");
		return src;
	}

	/**
	 * 膨胀处理 参数 3,3,1
	 */
	public void toDilate(Mat mat, int i, int j, int iterations) {
		Imgproc.dilate(mat, mat, Imgproc.getStructuringElement(
				Imgproc.MORPH_RECT, new Size(i, j)), new Point(-1, -1),
				iterations);
		Log.d(tag, "膨胀完成");
	}

	/**
	 * 腐蚀处理 参数3,3,1
	 */
	public void toErode(Mat mat, int i, int j, int iterations) {
		Imgproc.erode(mat, mat, Imgproc.getStructuringElement(
				Imgproc.MORPH_RECT, new Size(i, j)), new Point(-1, -1),
				iterations);
		Log.d(tag, "腐蚀完成");
	}

	/**
	 * 复制Mat对象
	 */
	public Mat cloneMat(Mat mat) {
		return mat.clone();
	}

	/**
	 * 生成纯色Mat对象
	 */
	public Mat makeBGRMat(int b, int g, int r) {
		return new Mat(360, 640, CvType.CV_8UC3, new Scalar(b, g, r));
	}

	/**
	 * 查找轮廓并返回轮廓数组 最好传入阈值图
	 */
	public List<MatOfPoint> findContoursList(Mat mat) {
		List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
		Mat hierarchy = new Mat();
		Imgproc.findContours(mat, contours, hierarchy, Imgproc.RETR_LIST,
				Imgproc.CHAIN_APPROX_SIMPLE);
		Log.d(tag, "找到轮廓" + contours.size() + "个");
		return contours;
	}

	/**
	 * 画出轮廓 根据轮廓数组在一张图上画 需指定数组下标
	 */
	public void drawContoursToMat(Mat mat, List<MatOfPoint> contours,
			int index, int b, int g, int r, int size) {
		Imgproc.drawContours(mat, contours, index, new Scalar(b, g, r, 0), size);
		Log.v(tag, "绘制第" + index + "个轮廓");
	}

	/**
	 * 二值化图片 参数i,j参考150,255
	 */
	public void toBinaryMat(Mat from, int i, int j) {
		Imgproc.threshold(from, from, i, j, Imgproc.THRESH_BINARY);
		Log.d(tag, "二值化完成");
	}

	/**
	 * 灰度化图像
	 */
	public void toGrayMat(Mat from) {
		Imgproc.cvtColor(from, from, Imgproc.COLOR_BGR2GRAY);
		Log.d(tag, "灰度化完成");
	}

	/**
	 * 图像颜色反转
	 */
	public void toReverseColorMat(Mat from) {
		Core.bitwise_not(from, from);
		Log.d(tag, "颜色反转完成");
	}

	/**
	 * 模糊图像,参数i,j,k参考5,5,0
	 */
	public void toGaussUnClearMat(Mat from, int i, int j, int k) {
		Imgproc.GaussianBlur(from, from, new Size(i, j), k);
		Log.d(tag, "高斯模糊完成");
	}

	/**
	 * 在图上写字
	 */
	public void printWordsOnMat(Mat mat, Point p, String text) {
		p.x = p.x - 100;
		Imgproc.putText(mat, text, p, 18, 0.5, new Scalar(255, 255, 255), 1);
	}

	/**
	 * 合并直线上的点[汇总]
	 *
	 * @param points
	 * @return
	 */
	public Point[] checkPoint(Point[] points) {
		int lastLength = -1;
		int thisLength = 0;
		Point[] lp = points;
		Point[] np;
		while (true) {
			np = checkPointOnce(lp);
			thisLength = np.length;
			if (thisLength == lastLength) {
				break;
			}
			lastLength = thisLength;
			lp = np;
		}
		Log.d(tag, "数组变化:" + points.length + " -> " + np.length);
		return np;
	}

	/**
	 * 合并直线上的点[分步]
	 */
	private Point[] checkPointOnce(Point[] points) {
		int length = points.length;
		boolean flag = false;// 是否找到可删除点
		if (length < 4) {
			return points;// 如果小于四个点 免了判断
		}
		label: for (int i = 0; i < length; i++) {// 得到点1
			for (int j = 0; j < length; j++) {// 得到点2
				if (j == i) {
					continue;
				}
				for (int k = 0; k < length; k++) {// 得到点3
					if (k == j || k == i) {
						continue;
					}
					// int slope = 0;//斜率
					double d1 = twoPointsAngel(points[i], points[j]);// i,j直线角度
					double d2 = twoPointsAngel(points[i], points[k]);// i,k直线角度
					double angelMin = d1 - d2;
					if (Math.abs(angelMin) < 10) {// 如果倾角非常接近,删除中间的点
						int needDelete = deleteMiddlePointToNull(points[i],
								points[j], points[k]);
						if (needDelete == 1) {
							points[i] = null;
						} else if (needDelete == 2) {
							points[j] = null;
						} else if (needDelete == 3) {
							points[k] = null;
						}
						flag = true;
						break label;
					}
				}
			}
		}
		if (flag) {
			Point[] newPoints = new Point[length - 1];
			int index = 0;
			for (Point p : points) {// 准备一个没有空值的新数组
				if (null != p) {
					newPoints[index] = p;
					index++;
				}
			}
			return newPoints;
		} else {
			return points;
		}
	}

	/**
	 * 删除三点中处于中间的点
	 */
	private int deleteMiddlePointToNull(Point p1, Point p2, Point p3) {
		double a = p1.x + p1.y;
		double b = p2.x + p2.y;
		double c = p3.x + p3.y;
		if ((a > b && b > c) || (a < b && b < c)) {// b在中间
			return 2;
		} else if ((c > a && a > b) || (c < a && a < b)) {// a在中间
			return 1;
		} else {
			return 3;
		}
	}

	/**
	 * 通过描边点得出形状
	 */
	public String findShape(Point[] checkedPoints) {
		int length = checkedPoints.length;
		if (length < 3) {
			return "ShapeError";
		} else if (length == 3) {
			return "SanJiao";
		} else if (length == 5) {
			return "WuJiao";
		} else if (length > 5) {
			return "YuanXing";
		} else if (length == 4) {// 四边形
			double d1 = twoPointsDistance(checkedPoints[0], checkedPoints[1]);
			double d2 = twoPointsDistance(checkedPoints[0], checkedPoints[2]);
			double d3 = twoPointsDistance(checkedPoints[0], checkedPoints[3]);
			Point[] p = new Point[2];
			// 找与第一个点相邻的两个点(舍弃最远的那个点)
			if (d1 > d2 && d1 > d3) {// d1最大,舍弃下标1
				p[0] = checkedPoints[2];
				p[1] = checkedPoints[3];
			} else if (d2 > d1 && d2 > d3) {// d2最大,舍弃下标2
				p[0] = checkedPoints[1];
				p[1] = checkedPoints[3];
			} else {
				p[0] = checkedPoints[1];
				p[1] = checkedPoints[2];
			}// 现在数组p中是两个最近的点
			double angelL1 = twoPointsAngel(checkedPoints[0], p[0]);
			double angelL2 = twoPointsAngel(checkedPoints[0], p[1]);
			double angelP = Math.abs(angelL1 - angelL2);
			Log.d(tag, String.format("四边形某顶点角度为%.2f", angelP));
			if (angelP > 80 && angelP < 100) {// 直角
				double dis1 = twoPointsDistance(checkedPoints[0], p[0]);
				double dis2 = twoPointsDistance(checkedPoints[0], p[1]);
				double distanceRatio = dis1 / dis2;
				Log.d(tag, String.format("四边形临边长度差距比为%.2f", distanceRatio));
				if (distanceRatio > 0.80 && distanceRatio < 1.20) {
					return "ZhengFang";
				} else {
					return "ChangFang";
				}
			} else {
				return "LingXing";
			}
		} else {
			return "ShapeError";
		}
	}

	/**
	 * 求两点之间距离
	 */
	private double twoPointsDistance(Point p1, Point p2) {
		return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
	}

	/**
	 * 求两点所在直线水平夹角
	 */
	private double twoPointsAngel(Point p1, Point p2) {
		if (p1.y == p2.y) {
			p1.y += 0.01;
		}
		return Math.toDegrees(Math.atan((p1.x - p2.x) / (p1.y - p2.y)));
	}

	/**
	 * 将Mat对象保存到文件系统
	 */
	public void saveMatAsPngFile(Mat mat, String filename) {
		Log.d(tag, "储存Mat");
		try {
			Imgcodecs.imwrite(Environment.getExternalStorageDirectory()
					+ "/CV/" + filename + ".png", mat);
			Log.i(tag, "Mat存储完成");
		} catch (Exception e) {
			Log.e(tag, "Mat存储出错");
		}
	}

	/**
	 * 将Mat对象保存到文件系统
	 */
	public void saveMatAsPngFileAndTimestamp(Mat mat, String filename) {
		Log.d(tag, "储存Mat");
		try {
			Imgcodecs.imwrite(Environment.getExternalStorageDirectory()
					+ "/CV/" + filename + System.currentTimeMillis() + ".jpg",
					mat);
			Log.i(tag, "Mat存储完成");
		} catch (Exception e) {
			Log.e(tag, "Mat存储出错");
		}
	}

//	private static final int[] RED = { 255, 0, 0 };
//	private static final int[] GREEN = { 0, 255, 0 };
//	private static final int[] BLUE = { 0, 0, 255 };
//	private static final int[] YELLOW = { 255, 255, 0 };
//	private static final int[] PIN = { 255, 0, 255 };
//	private static final int[] QING = { 0, 255, 255 };
//	private static final int[] BLACK = { 0, 0, 0 };
//	private static final int[] WHITE = { 255, 255, 255 };
//	
	private static final int[] RED = { 255, 0, 0 };
	private static final int[] GREEN = { 0, 255, 0 };
	private static final int[] BLUE = { 0, 0, 255 };
	private static final int[] YELLOW = { 255, 255, 0 };
	private static final int[] PIN = { 255, 0, 255 };
	private static final int[] QING = { 0, 255, 255 };
	private static final int[] BLACK = { 0, 0, 0 };
	private static final int[] WHITE = { 255, 255, 255 };
	private static ArrayList<int[]> COLORS = new ArrayList<>();

	static {
		COLORS.add(RED);
		COLORS.add(GREEN);
		COLORS.add(BLUE);
		COLORS.add(YELLOW);
		COLORS.add(PIN);
		COLORS.add(QING);
		COLORS.add(BLACK);
		COLORS.add(WHITE);
	}

	/**
	 * 确定形状的颜色
	 */
	public String findColor(Mat colorfulMat, Point[] checkedPoints) {
		Point centerPoint = getCenterPoint(checkedPoints);
		double[] colorBGR = colorfulMat.get((int) centerPoint.y,
				(int) centerPoint.x);
		double maxlightR = 0;// 确定亮度增大系数
		double maxlightG = 0;
		double maxlightB = 0;
		for (int y = 0; y < colorfulMat.height(); y++) {// 遍历图片 找到最亮的点做参考
			for (int x = 0; x < colorfulMat.width(); x++) {
				double pixle[] = colorfulMat.get(y, x);
				if (pixle[0] > maxlightB)
					maxlightB = pixle[0];
				if (pixle[1] > maxlightG)
					maxlightG = pixle[1];
				if (pixle[2] > maxlightR)
					maxlightR = pixle[2];
			}
		}// 用于去除亮度和色调影响
		maxlightR = 255 / maxlightR;
		maxlightG = 255 / maxlightG;
		maxlightB = 255 / maxlightB;
		int[] colorRGB = { (int) (colorBGR[2] * maxlightR),
				(int) (colorBGR[1] * maxlightG),
				(int) (colorBGR[0] * maxlightB) };
		int[] missNmuber = new int[8];
		for (int i = 0; i < 8; i++) {
			missNmuber[i] = colorMiss(colorRGB, COLORS.get(i));
		}
		int minIndex = 0;
		int minNumb = 999;
		for (int i = 0; i < missNmuber.length; i++) {
			if (missNmuber[i] < minNumb) {
				minNumb = missNmuber[i];
				minIndex = i;
			}
		}
		Log.d(tag, String.format(
				"R:%d, G:%d, B:%d, light:[%.2f * %.2f * %.2f]  =>  ",
				colorRGB[0], colorRGB[1], colorRGB[2], maxlightR, maxlightG,
				maxlightB));
		switch (minIndex) {
		case 0:
			return "Hong";
		case 1:
			return "Lv";
		case 2:
			return "Lan";
		case 3:
			return "Huang";
		case 4:
			return "Pin";
		case 5:
			return "Qing";
		case 6:
			return "Hei";
		case 7:
			return "Bai";
		default:
			return "ColorError";
		}
	}

	/**
	 * 计算颜色差距
	 */
	private int colorMiss(int[] c1, int[] c2) {
		return Math.abs(c1[0] - c2[2]) + Math.abs(c1[1] - c2[1])
				+ Math.abs(c1[2] - c2[0]);
	}

	/**
	 * 从文件系统图片读取Mat对象
	 */
	public Mat getImageAsFile(String filename) {// FIXME
		Log.d(tag, "读取Mat");
		Mat image = Imgcodecs.imread(Environment.getExternalStorageDirectory()
				+ "/CV/" + filename);
		Log.i(tag, "Mat读取完成");
		return image;
	}

}

以下是调用过程

public void colorAndShape() {// XXX opencv图形识别
		timer = new Timer();
		timer.schedule(new TimerTask() {
			OpenCvUtils cvut = new OpenCvUtils();
			ArrayList<ColorShapeInfo> resultList = new ArrayList<>();
			int erzhi = 130;

			@Override
			public void run() {//说明:主要的调用在这里哈,线程那块可以忽略不计
				Log.d("pzj", "in to cv !!");
				// new FileService().savePhoto(bitmap, "CV/doit.png");
				// Mat image = cvut.getImageAsFile("CV/doit.png");
				Mat image = cvut.bitmapToMat(bitmap);
				Mat m1 = cvut.cloneMat(image);
				Mat declarMat = cvut.cloneMat(image);
				cvut.toReverseColorMat(image);// 反色
				cvut.toGaussUnClearMat(declarMat, 9, 9, 9);// 模糊图像,用于推测颜色
				Mat m2 = cvut.cloneMat(declarMat);
				Mat srcWrite = cvut.cloneMat(declarMat);
				cvut.toDilate(image, 1, 1, 1);// 膨胀
				cvut.toGrayMat(image);// 灰度化
				Mat binary = cvut.cloneMat(image);
				cvut.toBinaryMat(binary, erzhi, 255);// 二值化
				cvut.coverBackGroundToBlack(binary);// 背景变黑
				cvut.saveMatAsPngFileAndTimestamp(binary, "binary");// SAVE
				List<MatOfPoint> contoursList = cvut.findContoursList(binary);// 找到轮廓
				for (int i = 0; i < contoursList.size()
						&& contoursList.size() <= 9; i++) {// 处理单个轮廓
					Mat mat = cvut.makeBGRMat(0, 0, 0);// 黑色背景
					cvut.drawContoursToMat(mat, contoursList, i, 0, 255, 0, 1);// 白色描边
					Point[] points = cvut.findp(mat);// 找到描边点
					Point[] checkedPoints = cvut.checkPoint(points);// 清除同一条直线上的点
					//ColorShapeInfo csi = ;
					//String shape = cvut.findShape(checkedPoints);// 确定形状
					//String color = cvut.findColor(declarMat, checkedPoints);// 确定颜色
					resultList.add(new ColorShapeInfo(cvut.findColor(declarMat, checkedPoints), cvut.findShape(checkedPoints)));
					for (int j = 0; j < checkedPoints.length; j++) {
						cvut.drawCircleByPoint(mat, checkedPoints[j]);
					}
					cvut.saveMatAsPngFile(mat, "Shape" + i);
				}
				if (resultList.size() != 0) {
					Log.d("CvResoult", Arrays.toString(resultList.toArray()));
					cvut.saveMatAsPngFileAndTimestamp(m2, "declar");// SAVE
					cvut.saveMatAsPngFileAndTimestamp(m1, "image");// SAVE
					timer.cancel();
					this.cancel();
				} else {
					resultList.clear();
					if (erzhi <= 180) {
						erzhi += 5;
					} else {
						erzhi = 150;
					}
				}
			}
		}, 0, 200);
	}

好吧,原谅我时间太久说不清细节了,我在这里简单讲一下逻辑,剩下的有需要的小伙伴可以对着我的代码和注释看,我的命名规范和注释还是有一定可读性的。
朋友圈①
朋友圈②
我又翻出了两张当时发的朋友圈,结合这两张图看一下,其实他们说明了我主要的处理过程。

在这里我采用自问自答的方式
1.“横向填充杂色”这个方法是用来做什么的:
这个方法适用于我当时做的东西,如图可见,这张图片在经过二值化处理后,是这个样子的
这个样子
那么opencv在不使用分类器的情况下,很难识别这张图片,当时时间紧张,都是算法实现,没空学python,没时间搞c的svm,可见外边一圈是白的,中间是个黑框框,黑框框里的东西才是我们想要的结果,顾想到了填充白色部分,他的原理是 从两边向中间扫描,白色就变成黑色,如果遇到了黑色,停止,去搞下一行。
好的,那么现在,经过了二值化(如果需要的话你可能会同时使用膨胀和腐蚀方法,详见别人的贴子)我们得到了这样的图,才好处理
这样的图
**2.**前面的反色方法做什么的 为什么一定要反色
反色其实在我的代码里最终体现为黑色变成了白色的交换,由经验可得,cv只去提取白色轮廓,并不提取黑色轮廓,如果上面这张图片黑白交换,是得不到正确结果的,原理不知,你可以探究一下。根据你的具体情况,判断你要不要也使用反色。

**3.**如何在这张图中提取形状
形状提取出来是这样的,没有白色的小圆圈,白色小圆圈是调用“画个圈圈”那个方法绘制的
只有白边,没有顶点
代码在上面是这样调用的:
List contoursList = cvut.findContoursList(binary);// 找到轮廓
好,轮廓提取出来,我又将这个轮廓绘制在了一张等大小的空白图上(其实是黑色,空黑),截图是三张图拼在了一起,并不是这样并不是这样
如上图只是为了展示效果,单独生成出来的,处理的时候每个轮廓分别存了一张图。我代码里有写的,重新把单个轮廓绘制在一张黑图上。
好,轮廓提取了出来,现在是黑纸白边,接下来的工作是找到顶点。上面有写,调用了这句:
Point[] points = cvut.findp(mat);// 找到描边点
我简单的画一下,参数要慢慢调,调完以后是这种情况比较理想
一般理想
这张图是我用截屏手工画的,对付看吧,顶点有点多,稍微少一点才是比较理想的状态。
当然,如果你只识别单个形状的话,大可调参数成cv只绘制四个顶点。
在我这个项目里,我需要过滤这些点,只留下顶点。参数慢慢调,上面有,我再贴一下
再贴一下
其实这些汉字的原版是另一个大神写的,也在csdn,我在他的基础上补充了一些自己的理解。时间久了 原谅我不找出处了,正在研究这块的小伙伴如果刚好看到,可以告诉我,我补上链接。如我所说,最重要的参数是角点品质因子。
好,那么如何取出顶点,我解释一下我上面的算法:三层循环,这个算法可以优化一多半,我知道,没做,爱咋咋地。每层循环都是逐个取点,比如取出1,2,3这三个点,然后取1,2,4然后1,2,5…以此类推,如果1,2,5这三个点在一条直线上,那么删除中间那个点,只留两边的点,这里又有两个问题:
**3.(1)**怎么判断在一条直线上:
计算斜率,没记错的话是x1/y1:x2/y2,求k值嘛,大家初中都学过,后来发现斜率并不好用,因为斜率不是线性的,靠近水平变化很小,靠近垂直变化很大(没记错的话)于是使用了java的求角度函数,就是k值转角度吧好像。如果1-2和1-5折两条直线的水平夹角非常接近,而他们又有共同点(就是1这个点呗)那我就认为这三个点在一条直线上,示意图:
示意图
于是我选择删除②点,因为最后要通过顶点数判断形状。
**3.(2)**如何知道2点在中间:
这个就比较简单了,你可以写个简单实现,将他们三个的x坐标排序,谁在中间就把谁变成null。
好,那么经过了一番遍历,数组中剩下了三角形的三个顶点,或者正方形的四个顶点
**4.**怎么证明三角形是三角形,正方形是正方形呢:
参见工具类中findShape方法,其实事情发展到今天,三角形就很好判断了,有三个顶点的图形就是三角形,四边形稍显复杂,因为他分正方形,长方形,菱形,平行四边形。而当时不需要判断平行四边形,所以我只介绍下如何区分前三种,这个搞懂了,平行四边形就不难了吧。首先判断了顶点的角度,这里引申出了一个问题,如何判断顶点角度:
**4.(1)**如何判断顶点角度:
在四边形里,找到一个点,随便找,然后求距离 |x1-x2|+|y1-y2| 虽然这不是求距离公式,但是距离越小这个值是不是就应该越小。所以四边形另外三个顶点中,距离更近的两个点就是这个点临边上的顶点,根据上面那张1-2 1-5那个很丑的手绘图所讲的故事,我们很容易通过java.math得出这个角度。
好,得出了顶点角度,我们可以大致判断,(因为我是拍照的图,如果是截屏那种,就不必那么“大致”)如果这个角度在85°-95°之间,那么这是一个直角四边形,如果不在这个范围,铁定是菱形。好,现在分开了菱形,那么正方形和长方形也好判断,之前不是已经求出了某个点的两个临近点,再计算一下距离,如果相差很小 a>0.9b && a<1.1b 比如这样,就是判断临边是否一样长,一样长的就是正方形呗。
**5.**如何判断图形颜色呢:
我们现在已经提取出来了图形,并且知道他所有的故事,唯独不知道颜色,好办,我们把他的每个顶点取出来,xy坐标取平均值 (x1+x2+x3+x4+x5)/5 这就是他的x轴中心点,再取出y轴中心点,就得到了图片中心点(好像不是严谨的)我们取出这个点的RGB,与基色对比,看跟谁接近。
**5.(1)**如何对比:
代码里有,我简单用三原色说明,红色是255,0,0绿色是0,255,0蓝色是0,0,255。假如你的图形只可能是这三种颜色,并且取出来了中心点,RGB的值为201,29,31。通过目测,我们很容易得到这是一个红色。如何计算呢,将每个可能的颜色分别和该图RGB取差值,差值越小,就越接近,比如|255-201|+|0-29|+|0-31|这个运算的结果,就是该图和红色的距离,这个距离越小,他就越接近红色。你用该图中心点与红绿蓝分别取出距离,会发现他离红色距离是最小的。
**5.(2)**颜色识别效果差:
如果你是我这种相机图的识别,而不是截屏图的识别,很容易遇到这种问题。打开PS,仔细分析图像数据,你会发现,本来应该是255,0,0的大红色,实际上R的值也就180+到头了,那么你需要参考这段代码:
这段代码
上面都有帖出来的哈,这段代码的大概意思是,遍历整张图,找到最红的红色点,比如是185,这说明本图的红色还不够红,本应是255,怎么就185呢,这是相机拍的色差,我对此作了弥补,尝试提高精度。对三原色每种颜色都进行一次审查(全图遍历),如发现最红的点R值是185,最绿的点G值是190,最蓝的点B值是225,那么我在计算5.(1)中颜色距离的时候,最好就加上一个系数。所有点的R值都乘以1.37(就是255/185),G值都乘以1.34(255/190),B值也是如此,乘上1.13 。可以一定程度上,弥补因为相机问题导致的色彩不够饱和的问题。这样结果更容易接近预期。
最后通过反向绘制,将拼音画到了原图上。
→→→→→题外话分割线→→→→→
当初搞这个东西废了好大的力气,这方面资源确实不太丰富,其中包括工具类中很多的方法的原型,都是翻的csdn中大神的贴子,时间久了,非常愧疚没将他们的链接贴上,虽然已经改的面目全非,还是非常感谢你们无私的分享,帮助了当时对cv一窍不通的我,也希望我写的这乱糟一团对他人有所帮助。

2020.10.15补充:大家总私信我要ColorShapeInfo这个类,我在评论区有解释,今天就勤快一回,把他贴到文章里吧。

//这其实就是只有两个属性的一个类,自动生成了getset方法和构造器
//我盲写的,如果还跑不通就自己再微调一下哈
class ColorShapeInfo{
    private String color;
    private String shape;

    public ColorShapeInfo() {
    }

    public ColorShapeInfo(String color, String shape) {
        this.color = color;
        this.shape = shape;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getShape() {
        return shape;
    }

    public void setShape(String shape) {
        this.shape = shape;
    }
}

另,本人考了专升本,现在(20年10月)在读本科软工了,几年前就说要写个svm版的,工作忙也没研究,近期争取把坑填上。谢谢大家。

  • 36
    点赞
  • 151
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 36
    评论
识别彩色二维码,可以使用Java中的ZXing库。ZXing是一个流行的开源库,用于识别各种类型的二维码,包括QR码和Data Matrix码等。ZXing库可以在Java中使用,并提供了一个简单的API来读取和解码二维码。 以下是一个简单的Java代码示例,用于识别彩色二维码: ```java import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import com.google.zxing.BinaryBitmap; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.NotFoundException; import com.google.zxing.RGBLuminanceSource; import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; public class ColorQRCodeReader { public static void main(String[] args) throws IOException, NotFoundException { File file = new File("colorQRCode.png"); BufferedImage image = ImageIO.read(file); int width = image.getWidth(); int height = image.getHeight(); int[] pixels = new int[width * height]; image.getRGB(0, 0, width, height, pixels, 0, width); LuminanceSource source = new RGBLuminanceSource(width, height, pixels); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); MultiFormatReader reader = new MultiFormatReader(); try { Result result = reader.decode(bitmap); System.out.println(result.getText()); } catch (ReaderException e) { e.printStackTrace(); } } } ``` 在这个例子中,我们首先读取彩色二维码图像,然后将其转换为像素数组。接下来,我们使用RGBLuminanceSource类创建一个LuminanceSource对象,该对象包含图像的亮度信息。然后,我们使用HybridBinarizer类创建一个BinaryBitmap对象,该对象可以用于读取和解码二维码。最后,我们使用MultiFormatReader类读取二维码数据,并将其打印到控制台上。 请注意,这个例子假定彩色二维码图像是一个RGB图像。如果彩色二维码图像不是一个RGB图像,则需要相应地调整代码。例如,如果彩色二维码图像是一个CMYK图像,则需要使用CMYKLuminanceSource类代替RGBLuminanceSource类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

控场的朴哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值