App 基于手机壳颜色换肤?先尝试一下用 KMeans 来提取图像中的主色
break;}times++;}
//update the result imageList<Scalar> colors = new ArrayList<Scalar>();for(ClusterCenter cc : clusterCenterList) {
colors.add(cc.color);}return colors;}
private boolean isStop(double[][] oldClusterCenterColors, double[][] newClusterCenterColors) {boolean stop = false;for (int i = 0; i < oldClusterCenterColors.length; i++) {if (oldClusterCenterColors[i][0] == newClusterCenterColors[i][0] &&oldClusterCenterColors[i][1] == newClusterCenterColors[i][1] &&oldClusterCenterColors[i][2] == newClusterCenterColors[i][2]) {stop = true;break;}}return stop;}
/**
update the cluster index by distance value*/private void stepClusters() {// initialize the clusters for each pointdouble[] clusterDisValues = new double[clusterCenterList.size()];for(int i=0; i<pointList.size(); i++){for(int j=0; j<clusterCenterList.size(); j++){clusterDisValues[j] = calculateEuclideanDistance(pointList.get(i), clusterCenterList.get(j));}pointList.get(i).clusterIndex = (getCloserCluster(clusterDisValues));}
}
/**
using cluster color of each point to update cluster center color
@return*/private double[][] reCalculateClusterCenters() {
// clear the points nowfor(int i=0; i<clusterCenterList.size(); i++){clusterCenterList.get(i).numOfPoints = 0;}
// recalculate the sum and total of points for each clusterdouble[] redSums = new double[numOfCluster];double[] greenSum = new double[numOfCluster];double[] blueSum = new double[numOfCluster];for(int i=0; i<pointList.size(); i++){int cIndex = (int)pointList.get(i).clusterIndex;clusterCenterList.get(cIndex).numOfPoints++;int tr = pointList.get(i).pixelColor.red;int tg = pointList.get(i).pixelColor.green;int tb = pointList.get(i).pixelColor.blue;redSums[cIndex] += tr;greenSum[cIndex] += tg;blueSum[cIndex] += tb;}
double[][] oldClusterCentersColors = new double[clusterCenterList.size()][3];for(int i=0; i<clusterCenterList.size(); i++){double sum = clusterCenterList.get(i).numOfPoints;int cIndex = clusterCenterList.get(i).cIndex;int red = (int)(greenSum[cIndex]/sum);int green = (int)(greenSum[cIndex]/sum);int blue = (int)(blueSum[cIndex]/sum);cluster
CenterList.get(i).color = new Scalar(red, green, blue);oldClusterCentersColors[i][0] = red;oldClusterCentersColors[i][0] = green;oldClusterCentersColors[i][0] = blue;}
return oldClusterCentersColors;}
/***
@param clusterDisValues
@return*/private double getCloserCluster(double[] clusterDisValues) {double min = clusterDisValues[0];int clusterIndex = 0;for(int i=0; i<clusterDisValues.length; i++){if(min > clusterDisValues[i]){min = clusterDisValues[i];clusterIndex = i;}}return clusterIndex;}
/***
@param p
@param c
@return distance value*/private double calculateEuclideanDistance(ClusterPoint p, ClusterCenter c) {int pr = p.pixelColor.red;int pg = p.pixelColor.green;int pb = p.pixelColor.blue;int cr = c.color.red;int cg = c.color.green;int cb = c.color.blue;return Math.sqrt(Math.pow((pr - cr), 2.0) + Math.pow((pg - cg), 2.0) + Math.pow((pb - cb), 2.0));}
在 Android 中使用该算法来提取主色:
完整的算法实现可以在:github.com/imageproces… 找到,它是一个典型的 KMeans 算法。
我们的算法中,K 默认值是 5,当然也可以自己指定。
以上算法目前在 demo 上耗时蛮久,不过可以有优化空间。例如,可以使用 RxJava 在 computation 线程中做复杂的计算操作然后切换回 ui 线程。亦或者可以使用类似 Kotlin 的 Coroutines 来做复杂的计算操作然后切换回 ui 线程。总结
提取图像中的主色,还有其他算法例如八叉树等,在 Android 中也可以使用 Palette 的 API 来实现。cv4j?是gloomyfish和我一起开发的图像处理库,纯 java 实现,我们已经分离了一个 Android 版本和一个 Java 版本。如果您想看该系列先前的文章可以访问下面的文集: www.jianshu.com/nb/10401400最后提醒一句,作为程序员,还是要多健身。
评论