Android中OpenCV Kmeans颜色量化仅输出黑白图像求助
Android中OpenCV Kmeans颜色量化输出黑白的问题修复
问题描述
在Android Studio中使用OpenCV的Kmeans算法实现颜色量化时,无论初始还是更新代码,输出图像始终为黑白。
初始代码
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData()); ny = new Mat(); Utils.bitmapToMat(bitmap, ny); Mat bgrImage = new Mat(); Imgproc.cvtColor(ny, bgrImage, Imgproc.COLOR_RGB2BGR); bgrImage.convertTo(bgrImage, CvType.CV_32F, 1.0 / 255.0); Mat reshapedImage = bgrImage.reshape(3,bgrImage.rows()*bgrImage.cols() ); Mat labels = new Mat(); Mat centers = new Mat(); ArrayList<Integer> labelsList = new ArrayList<>(); int K = 32; // 聚类数量(颜色数) TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 400, 2.0); Core.kmeans(reshapedImage, K, labels, criteria, 1, Core.KMEANS_PP_CENTERS,centers); for (int i = 0; i < labels.rows(); i++) { for (int j = 0; j < labels.cols(); j++) { labelsList.add((int) labels.get(i, j)[0]); } } int n = labels.cols() * labels.rows(); for (int i = 0; i < n; ++i) { double[] color = centers.get(labelsList.get(i), 0); labels.put(i, 0, color); } labels.convertTo(labels, CvType.CV_8U); Core.normalize(labels, labels, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC3); Imgproc.cvtColor(labels,labels,Imgproc.COLOR_GRAY2RGB); labels = labels.reshape(3,bgrImage.rows()); Mat blured = new Mat(); int kernelsz = 5; Imgproc.medianBlur(labels, blured, kernelsz); Bitmap resultBitmap = Bitmap.createBitmap(blured.cols(), blured.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(blured, resultBitmap); mainview.setImageBitmap(resultBitmap);
更新后代码
调整代码后输出仍为黑白:
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData()); ny = new Mat(); Utils.bitmapToMat(bitmap, ny); Mat bgrImage = new Mat(); Imgproc.cvtColor(ny, bgrImage, Imgproc.COLOR_RGB2BGR); bgrImage.convertTo(bgrImage, CvType.CV_32F, 1.0 /255.0); Mat reshapedImage = bgrImage.reshape(1,bgrImage.rows()*bgrImage.cols() ); Mat labels = new Mat(); Mat centers = new Mat(); ArrayList<Integer> labelsList = new ArrayList<>(); int K = 4; // 聚类数量(颜色数) TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER,400, 2.0); Core.kmeans(reshapedImage, K, labels, criteria, 1, Core.KMEANS_PP_CENTERS,centers); centers.convertTo(centers, CvType.CV_8UC1, 255.0); centers.reshape(3); List<Mat> clusters = new ArrayList<Mat>(); for(int i = 0; i < centers.rows(); i++) { clusters.add(Mat.zeros(bgrImage.size(),bgrImage.type())); } Map<Integer, Integer> counts = new HashMap<Integer, Integer>(); for(int i = 0; i < centers.rows(); i++) counts.put(i, 0); int rows = 0; for(int y = 0; y < bgrImage.rows(); y++) { for(int x = 0; x < bgrImage.cols(); x++) { int label = (int)labels.get(rows, 0)[0]; int r = (int)centers.get(label, 2)[0]; int g = (int)centers.get(label, 1)[0]; int b = (int)centers.get(label, 0)[0]; counts.put(label, counts.get(label) + 1); clusters.get(label).put(y, x, b, g, r); rows++; } } Mat coloredMat = new Mat(); coloredMat = clusters.get(3); coloredMat.convertTo(coloredMat,CvType.CV_8UC4); Bitmap resultBitmap = Bitmap.createBitmap(coloredMat.cols(), coloredMat.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(coloredMat, resultBitmap); anaekran.setImageBitmap(resultBitmap);
问题根源
初始代码问题
reshape参数错误:Kmeans要求输入为N行3列的矩阵(N为像素总数,每行对应一个像素的BGR三通道值),初始代码中reshape(3, ...)导致矩阵结构不符合算法要求,聚类逻辑失效。- 结果生成逻辑错误:直接修改
labels矩阵并做灰度转RGB的操作,完全偏离了“用聚类中心颜色替换原像素”的正确流程。
更新后代码问题
centers.reshape未赋值:该方法返回新的Mat对象,但未重新赋值给centers,导致后续读取颜色值时矩阵结构错误。- 仅显示单个聚类:
coloredMat = clusters.get(3)只提取了第4个聚类的区域,而非将所有像素替换为对应聚类颜色的完整图像。
修复后的代码
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData()); Mat srcMat = new Mat(); Utils.bitmapToMat(bitmap, srcMat); // 转换为BGR格式并归一化到0-1的浮点范围 Mat bgrMat = new Mat(); Imgproc.cvtColor(srcMat, bgrMat, Imgproc.COLOR_RGB2BGR); bgrMat.convertTo(bgrMat, CvType.CV_32F, 1.0 / 255.0); // 转换为Kmeans所需的N×3矩阵(N为像素总数) int totalPixels = bgrMat.rows() * bgrMat.cols(); Mat reshapedMat = bgrMat.reshape(1, totalPixels); // Kmeans参数配置 int K = 4; Mat labels = new Mat(); Mat centers = new Mat(); TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 400, 1e-4); // 多次初始化提升聚类稳定性 Core.kmeans(reshapedMat, K, labels, criteria, 3, Core.KMEANS_PP_CENTERS, centers); // 将聚类中心转换为0-255的8位BGR格式 centers.convertTo(centers, CvType.CV_8UC1, 255.0); // 创建结果图像,用聚类中心颜色替换对应像素 Mat resultMat = Mat.zeros(bgrMat.size(), CvType.CV_8UC3); for (int i = 0; i < totalPixels; i++) { int label = (int) labels.get(i, 0)[0]; // 获取当前聚类中心的BGR颜色值 byte[] color = new byte[3]; centers.get(label, 0, color); // 计算像素坐标 int y = i / bgrMat.cols(); int x = i % bgrMat.cols(); // 设置结果图像像素 resultMat.put(y, x, color); } // 转换为RGB格式适配Android Bitmap Imgproc.cvtColor(resultMat, resultMat, Imgproc.COLOR_BGR2RGB); // 生成Bitmap并显示 Bitmap resultBitmap = Bitmap.createBitmap(resultMat.cols(), resultMat.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(resultMat, resultBitmap); anaekran.setImageBitmap(resultBitmap); // 释放OpenCV资源,避免内存泄漏 srcMat.release(); bgrMat.release(); reshapedMat.release(); labels.release(); centers.release(); resultMat.release();
关键修复点
- 修正
reshape参数,确保输入Kmeans的矩阵为N×3的像素特征矩阵; - 正确处理聚类中心的颜色值读取,避免矩阵结构错误;
- 生成完整的结果图像,将所有像素替换为对应聚类中心的颜色;
- 完成BGR到RGB的颜色空间转换,适配Android Bitmap的颜色格式;
- 添加资源释放逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Tndstn
相关产品推荐
相关产品推荐

