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.statistics; 011 012import java.util.Arrays; 013 014/** 015 * This class calculates a color histogram of a set of colors (i.e., a color image). 016 * Only the unique colors are accounted for. Colors are supplied as ARGB-encoded 017 * integers (A = alpha values being ignored). 018 * Colors are internally sorted by their frequency (in descending order). 019 * 020 * @author WB 021 * @version 2017/01/04 022 */ 023public class ColorHistogram { 024 025 private final ColorNode[] colornodes; 026 027 public ColorHistogram(int[] pixelsOrig) { 028 this(pixelsOrig, false); 029 } 030 031 /** 032 * Creates a color histogram instance from the supplied sequence 033 * of color pixel values (assumed to be ARGB-encoded integers). 034 * The original pixel values are not modified. 035 * 036 * @param pixelsOrig Original pixel values (not modified). 037 * @param sortByFrequency Pass true to sort the final colors by descending frequency. 038 */ 039 public ColorHistogram(int[] pixelsOrig, boolean sortByFrequency) { 040 int[] pixels = new int[pixelsOrig.length]; 041 for (int i = 0; i < pixels.length; i++) { 042 pixels[i] = 0xFFFFFF & pixelsOrig[i]; // remove nonzero alpha components 043 } 044 045 Arrays.sort(pixels); // this is why we need a copy of the input array 046 047 // count unique colors: 048 int k = -1; // current color index 049 int curColor = -1; 050 for (int i = 0; i < pixels.length; i++) { 051 if (pixels[i] != curColor) { 052 k++; 053 curColor = pixels[i]; 054 } 055 } 056 int nUnique = k + 1; // number of unique colors 057 058 colornodes = new ColorNode[nUnique]; 059 060 // tabulate and find frequency of unique colors: 061 k = -1; // current color index 062 curColor = -1; 063 for (int i = 0; i < pixels.length; i++) { 064 if (pixels[i] != curColor) { // found a new color 065 k++; 066 curColor = pixels[i]; 067 colornodes[k] = new ColorNode(curColor); 068 } 069 else { // still with the previous color 070 colornodes[k].add(1); 071 } 072 } 073 074 if (sortByFrequency) 075 Arrays.sort(colornodes); // sort unique colors by descending frequency 076 } 077 078 /** 079 * Returns the number of unique colors. 080 * @return The number of unique colors. 081 */ 082 public int getNumberOfColors() { 083 return colornodes.length; 084 } 085 086 /** 087 * Returns the unique color with the given index. 088 * Colors are sorted by (decreasing) frequency. 089 * @param index The color index. 090 * @return The color, encoded as an ARGB integer (A is zero). 091 */ 092 public int getColor(int index) { 093 return colornodes[index].rgb; 094 } 095 096 /** 097 * Returns the frequency of the unique color with the given index. 098 * Colors are sorted by (decreasing) frequency. 099 * @param index The color index. 100 * @return The frequency of the color. 101 */ 102 public int getCount(int index) { 103 return colornodes[index].count; 104 } 105 106 /** 107 * Lists the unique colors to System.out (intended for 108 * debugging only). 109 */ 110 public void listUniqueColors() { 111 for (ColorNode cn : colornodes) { 112 System.out.println(cn.toString()); 113 } 114 } 115 116 // -------------------------------------------------------------------------------- 117 118 private class ColorNode implements Comparable<ColorNode> { 119 private final int rgb; 120 private int count; 121 122 ColorNode(int rgb) { 123 this.rgb = rgb; 124 this.count = 1; 125 } 126 127 void add(int n) { 128 count = count + n; 129 } 130 131 public String toString() { 132 return String.format(ColorNode.class.getSimpleName() + " rgb=%d count=%d", rgb, count); 133 } 134 135 @Override 136 public int compareTo(ColorNode c2) { // to sort by count (high counts first) 137 if (this.count > c2.count) return -1; 138 if (this.count < c2.count) return 1; 139 else return 0; 140 } 141 } 142 143}