Hi!
I’m trying to use kmeans to determine the prevailing colors in an image.
The main idea: there is a list of URLs in the format “content://media/external/images/media/65”. I read the data from the local storage, transform it into a bitmap, send this bitmap for clustering, get the primary colors in the image and display them in the application.
I’ll show you the code of a minimal project in which I’m testing clustering.
I changed the opencv version, the criteria for kmeans - don’t work.
I tried to use kmeans in python, everything works there. Not in java.
compileSdk = 34.
opencv = “4.10.0”
Dependencies ok.
package com.fuxkinghatred.testopencv;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.TermCriteria;
import org.opencv.imgproc.Imgproc;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
if (loadOpenCV()) return;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.a3);
if (bitmap == null) {
Log.d(TAG, "onCreate: Failed to upload image");
return;
}
Mat mat = new Mat();
Utils.bitmapToMat(bitmap, mat);
Log.d(TAG, "onCreate: mat: " + mat);
List<Scalar> dominantColors = getDominantColors(mat, 3);
for (Scalar color : dominantColors) {
Log.d(TAG, "onCreate: dominantColors: color : " + color);
}
String colorString = "";
for (Scalar color : dominantColors) {
Log.d(TAG, "onCreate: dominantColors: color : " + color);
}
Log.d(TAG, "onCreate: colorString" + colorString);
}
private List<Scalar> getDominantColors(Mat image, int k) {
if (image.channels() != 3) {
Imgproc.cvtColor(image, image, Imgproc.COLOR_RGBA2RGB);
}
List<Mat> channels = new ArrayList<>();
Core.split(image, channels);
Mat rgb = new Mat();
Core.merge(channels.subList(0, 3), rgb);
Mat pixels = rgb.reshape(1, rgb.rows() * rgb.cols());
Log.d(TAG, "Pixels dimensions after reshape: " + pixels.rows() + " x " + pixels.cols());
pixels.convertTo(pixels, CvType.CV_32F);
Core.normalize(pixels, pixels, 0, 1, Core.NORM_MINMAX);
Log.d(TAG, "Normalized pixels data:");
for (int i = 0; i < Math.min(10, pixels.rows()); i++) {
Log.d(TAG, Arrays.toString(pixels.get(i, 0)));
}
Log.d(TAG, "Pixels dimensions: " + pixels.rows() + " x " + pixels.cols());
Log.d(TAG, "Pixels type: " + pixels.type());
Mat labels = new Mat();
Mat centers = new Mat();
TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 200, 0.001);
int attempts = 20;
Log.d(TAG, "getDominantColors: criteria: " + criteria);
Log.d(TAG, "pixels data before kmeans:");
for (int i = 0; i < Math.min(10, pixels.rows()); i++) {
Log.d(TAG, Arrays.toString(pixels.get(i, 0)));
}
Log.d(TAG, "Before kmeans: centers.rows(): " + centers.rows() + ", centers.cols(): " + centers.cols());
try {
Core.kmeans(pixels, k, labels, criteria, attempts, Core.KMEANS_PP_CENTERS);
} catch (Exception e) {
Log.e(TAG, "Core.kmeans failed: " + e.getMessage(), e);
return new ArrayList<>();
}
List<Scalar> dominantColors = new ArrayList<>();
for (int i = 0; i < centers.rows(); i++) {
double[] data = centers.get(i, 0);
dominantColors.add(new Scalar(data[0], data[1], data[2]));
Log.d(TAG, "getDominantColors: dominantColors.get(" + i + "): " + dominantColors.get(i));
}
Log.d(TAG, "After kmeans: centers.rows(): " + centers.rows() + ", centers.cols(): " + centers.cols());
Log.d(TAG, "After kmeans: dominantColors.size(): " + dominantColors.size());
return dominantColors;
}
private boolean loadOpenCV() {
if (OpenCVLoader.initLocal())
Log.d(TAG, "OpenCV loaded successfully");
else {
Log.e(TAG, "OpenCV initialization failed!");
Toast.makeText(this, "OpenCV initialization failed!", Toast.LENGTH_LONG).show();
return true;
}
return false;
}
}
Image;
Logcat:
20:04:08.363 MainActivity D OpenCV loaded successfully
20:04:08.481 MainActivity D onCreate: mat: Mat [ 1280*1920*CV_8UC4, isCont=true, isSubmat=false, nativeObj=0xe3c878a0, dataAddr=0xb7980000 ]
20:04:08.558 MainActivity D Pixels dimensions after reshape: 2457600 x 3
20:04:08.625 MainActivity D Normalized pixels data:
20:04:08.626 MainActivity D [0.4392157196998596]
20:04:08.628 MainActivity D [0.4392157196998596]
20:04:08.628 MainActivity D [0.44705885648727417]
20:04:08.629 MainActivity D [0.4549019932746887]
20:04:08.630 MainActivity D [0.46666669845581055]
20:04:08.630 MainActivity D [0.4784314036369324]
20:04:08.631 MainActivity D [0.4941176772117615]
20:04:08.632 MainActivity D [0.5098039507865906]
20:04:08.632 MainActivity D [0.5254902243614197]
20:04:08.633 MainActivity D Pixels dimensions: 2457600 x 3
20:04:08.633 MainActivity D Pixels type: 5
20:04:08.634 MainActivity D getDominantColors: criteria: { type: 2, maxCount: 500, epsilon: 1.0E-4}
20:04:08.635 MainActivity D pixels data before kmeans:
20:04:08.635 MainActivity D [0.4392157196998596]
20:04:08.636 MainActivity D [0.4392157196998596]
20:04:08.637 MainActivity D [0.4392157196998596]
20:04:08.637 MainActivity D [0.44705885648727417]
20:04:08.638 MainActivity D [0.4549019932746887]
20:04:08.639 MainActivity D [0.46666669845581055]
20:04:08.639 MainActivity D [0.4784314036369324]
20:04:08.640 MainActivity D [0.4941176772117615]
20:04:08.641 MainActivity D [0.5098039507865906]
20:04:08.641 MainActivity D [0.5254902243614197]
20:04:08.642 MainActivity D Before kmeans: centers.rows(): 0, centers.cols(): 0
20:05:07.164 MainActivity D After kmeans: centers.rows(): 0, centers.cols(): 0
20:05:07.164 MainActivity D After kmeans: dominantColors.size(): 0
20:05:07.164 MainActivity D onCreate: colorString