纯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++) {// 得到点1for (int j = 0; j < length; j++) {// 得到点2if (j == i) {continue;}for (int k = 0; k < length; k++) {// 得到点3if (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最大,舍弃下标1p[0] = checkedPoints[2];p[1] = checkedPoints[3];} else if (d2 > d1 && d2 > d3) {// d2最大,舍弃下标2p[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) {// FIXMELog.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;@Overridepublic 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");// SAVEList<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");// SAVEcvut.saveMatAsPngFileAndTimestamp(m1, "image");// SAVEtimer.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版的,工作忙也没研究,近期争取把坑填上。谢谢大家。

OpenCV形状颜色识别Java,Android相关推荐

  1. Opencv之颜色识别

    用Opencv之颜色识别 1.以下是我的基本流程: 读入图像 图像转成HSV 高斯滤波 筛选需要识别的颜色 腐蚀操作 找出轮廓 画出轮廓 接下来是我的总代码: import cv2 import nu ...

  2. opencv做颜色识别(python)

    使用Raspberry摄像头抓图然后进行颜色识别. #!/usr/bin/python # -*- coding: UTF-8 -*- import sys import time from pica ...

  3. android opencv颜色识别,OpenCV学习——颜色识别的简单应用(一)

    第1步:获取到桌球台的图像信息 需要获取桌面固定区域的实时图像(这里为了方便,直接截取了一张图片) 第2步:识别出球杆和白球 创建滑条from cv2 import cv2 import numpy ...

  4. opencv颜色识别java,Opencv颜色识别与追踪

    //---------------------------------[头文件.命名空间包含部分]----------------------------//描述:包含程序所使用的头文件和命名空间// ...

  5. 基于Opencv的颜色识别

    彩色模型   数字图像处理中常用的采用模型是RGB(红,绿,蓝)模型和HSV(色调,饱和度,亮度),RGB广泛应用于彩色监视器和彩色视频摄像机,我们平时的图片一般都是RGB模型.而HSV模型更符合人描 ...

  6. 【OpenCV】HSV颜色识别-HSV基本颜色分量范围

    出处:百度文库 一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对应的HSV分量需要给定一个严格的范围,下面是通过实验计算的模糊范围(准确的范围在网上都没有给出). H:  0 ...

  7. 【Opencv小项目 1】Opencv实现简单颜色识别

    参考 Opencv简单颜色识别 Youtube教学视频 BGR HSV颜色模型 步骤 一. BGR 和 HSV 颜色模型 BGR Model BGR模型表示三种颜色通道:红.绿.蓝,采用BGR模型的图 ...

  8. openCV-python实现颜色识别

    本文将介绍使用OpenCV实现颜色识别的详细步骤 + 代码. 背景介绍 在截取出模板匹配到的logo区域之后,需要判断logo是什么颜色.本案例中要识别的对象是纯色的所以适用下面的颜色识别方法,有不同 ...

  9. java识别图片中的图形形状_基于Java+OpenCV技术对几何图像颜色与形状识别

    基于 Java+OpenCV 技术对几何图像颜色与形状识别 杨思阳 黄军 吴春秋 (黔东南民族职业技术学院,贵州 凯里 556000) [摘 要] 通过 Java 调用 OpenCV 视觉库实现几何图 ...

最新文章

  1. java数独中数独空格初始化,java高手近解决数独问题,看你是不是高手!
  2. UA MATH564 概率论VI 数理统计基础3 卡方分布上
  3. 2013年4月计算机一级试题 答案,9月计算机一级考试试题含答案
  4. CRM Fiori页面返回的元数据解析
  5. 「云」发展的怎么样了?
  6. 具有InlfuxDB的Spring Boot和Micrometer第1部分:基础项目
  7. Oracle自定义函数(不断更新)
  8. python数据框的横向贾总_[Spark][Python]DataFrame的左右连接例子
  9. sqlserver使用depart获取当前日期月份及通过拼接得到日期时间
  10. python有限元传热求解_二维稳态热传导基本方程的有限元求解(2)
  11. 阿里巴巴中台技术架构实践与思考
  12. 事务及其传播行为的通俗易懂的讲解
  13. ckplayer ajax,旋风解析
  14. 交易系统开发(六)——HFT高频交易
  15. Stata: 空间计量模型溢出效应的动态呈现
  16. abc类地址是如何划分的? (转)
  17. [转帖]任正非管理思想
  18. 推理悬疑侦探小说大全合集隐私政策
  19. Qiyuan - 接小球游戏4.0
  20. 人生忠告:给男人和女人(ZT)

热门文章

  1. 淘宝UWP中的100个为什么
  2. vscode关闭源代码管理处的提示
  3. ASP网上贴吧系统的设计与实现
  4. 2021寒假MISC打卡DAY4
  5. UIBOT的简单使用
  6. 回:《孩子感冒了该吃什么药?说给程序员朋友的话》——勿以生命赌中医!...
  7. WAX 区块链发行的 Topps 数字收藏卡将在美国沃尔玛和塔吉特发售
  8. 移动、电信、联通:5G To B的花式解法
  9. MSP430学习总结(二)——GPIO
  10. google hacker语法