You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);

问题根源

  1. 初始代码问题

    • reshape参数错误:Kmeans要求输入为N行3列的矩阵(N为像素总数,每行对应一个像素的BGR三通道值),初始代码中reshape(3, ...)导致矩阵结构不符合算法要求,聚类逻辑失效。
    • 结果生成逻辑错误:直接修改labels矩阵并做灰度转RGB的操作,完全偏离了“用聚类中心颜色替换原像素”的正确流程。
  2. 更新后代码问题

    • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:25:00