001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit http://imagingbook.com for additional details. 008 *******************************************************************************/ 009 010package imagingbook.pub.color.quantize; 011 012import java.util.ArrayList; 013import java.util.LinkedList; 014import java.util.List; 015 016import org.apache.commons.math3.ml.clustering.CentroidCluster; 017import org.apache.commons.math3.ml.clustering.DoublePoint; 018import org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer; 019 020 021/** 022 * This class implements color quantization by k-means clustering 023 * of image pixels in RGB color space. It uses the Apache Commons 024 * Math class {@link KMeansPlusPlusClusterer} to perform the 025 * clustering. 026 * 027 * @author WB 028 * @version 2017/01/04 029 */ 030public class KMeansClusteringQuantizerApache extends ColorQuantizer { 031 032 private final Parameters params; 033 private final int[][] colormap; 034 035// private final Clusterer<DoublePoint> clusterer; 036 private final List<CentroidCluster<DoublePoint>> centers; 037 038 public enum SamplingMethod { 039 Random, Most_Frequent 040 }; 041 042 public static class Parameters { 043 /** Maximum number of quantized colors. */ 044 public int maxColors = 16; 045 /** Maximum number of clustering iterations */ 046 public int maxIterations = 500; 047 048 void check() { 049 if (maxColors < 2 || maxColors > 256 || maxIterations < 1) { 050 throw new IllegalArgumentException(); 051 } 052 } 053 } 054 055 // -------------------------------------------------------------- 056 057 /** 058 * Creates a new quantizer instance from the supplied sequence 059 * of color values (assumed to be ARGB-encoded integers). 060 * @param pixels Sequence of input color values. 061 * @param params Parameter object. 062 */ 063 public KMeansClusteringQuantizerApache(int[] pixels, Parameters params) { 064 params.check(); 065 this.params = params; 066 centers = cluster(pixels); 067 colormap = makeColorMap(); 068 } 069 070 public KMeansClusteringQuantizerApache(int[] pixels) { 071 this(pixels, new Parameters()); 072 } 073 074 // -------------------------------------------------------------- 075 076 private List<CentroidCluster<DoublePoint>> cluster(int[] pixels) { 077 KMeansPlusPlusClusterer<DoublePoint> clusterer = 078 new KMeansPlusPlusClusterer<>(params.maxColors, params.maxIterations); 079 080 List<DoublePoint> points = new ArrayList<>(); 081 for (int i = 0; i < pixels.length; i++) { 082 points.add(new DoublePoint(intToRgbDouble(pixels[i]))); 083 } 084 085 return clusterer.cluster(points); 086 } 087 088 // -------------------------------------------------------------- 089 090 private double[] intToRgbDouble(int rgb) { 091 int red = ((rgb >> 16) & 0xFF); 092 int grn = ((rgb >> 8) & 0xFF); 093 int blu = (rgb & 0xFF); 094 return new double[] {red, grn, blu}; 095 } 096 097 private int[][] makeColorMap() { 098 List<int[]> colList = new LinkedList<>(); 099 100 for (CentroidCluster<DoublePoint> ctr : centers) { 101 double[] c = ctr.getCenter().getPoint(); 102 int red = (int) Math.round(c[0]); 103 int grn = (int) Math.round(c[1]); 104 int blu = (int) Math.round(c[2]); 105 colList.add(new int[] {red, grn, blu}); 106 } 107 108 return colList.toArray(new int[0][]); 109 } 110 111 /** 112 * Lists the color clusters to System.out (intended for debugging only). 113 */ 114// public void listClusters() { 115// for (Cluster c : clusters) { 116// System.out.println(c.toString()); 117// } 118// } 119 120 121 // ------- methods required by abstract super class ----------------------- 122 123 @Override 124 public int[][] getColorMap() { 125 return colormap; 126 } 127 128} 129