@Override public void applyInPlace(FastBitmap fastBitmap) { if (fastBitmap.isRGB()) { fastBitmap.toGrayscale(); } if (invert) new Invert().applyInPlace(fastBitmap); int size = fastBitmap.getWidth() * fastBitmap.getHeight(); int min = ImageStatistics.Minimum(fastBitmap); int max = ImageStatistics.Maximum(fastBitmap); fastBitmap.toRGB(); for (int i = 0; i < size; i++) { int[] rgb = GrayscaleToHeatMap(fastBitmap.getRed(i), min, max); fastBitmap.setRGB(i, rgb); } }
/** * Calculate binarization threshold for the given image. * * @param fastBitmap FastBitmap. * @return Threshold value. */ public int CalculateThreshold(FastBitmap fastBitmap) { ImageStatistics stat = new ImageStatistics(fastBitmap); Histogram hist = stat.getHistogramGray(); int[] histogram = hist.getValues(); int total = fastBitmap.getWidth() * fastBitmap.getHeight(); double sum = 0; for (int i = 0; i < 256; i++) sum += i * histogram[i]; double sumB = 0; int wB = 0; int wF = 0; double varMax = 0; int threshold = 0; for (int i = 0; i < 256; i++) { wB += histogram[i]; if (wB == 0) continue; wF = total - wB; if (wF == 0) break; sumB += (double) (i * histogram[i]); double mB = sumB / wB; double mF = (sum - sumB) / wF; double varBetween = (double) wB * (double) wF * (mB - mF) * (mB - mF); if (varBetween > varMax) { varMax = varBetween; threshold = i; } } return threshold; }
/** * Calculate binarization threshold for the given image. * * @param fastBitmap FastBitmap. * @return Threshold value. */ public int CalculateThreshold(FastBitmap fastBitmap) { ImageStatistics stat = new ImageStatistics(fastBitmap); ImageHistogram hist = stat.getHistogramGray(); int[] histogram = hist.getValues(); // Normalize histogram, that is makes the sum of all bins equal to 1. double sum = 0; for (int i = 0; i < histogram.length; ++i) { sum += histogram[i]; } if (sum == 0) { // This should not normally happen, but... throw new IllegalArgumentException("Empty histogram: sum of all bins is zero."); } double[] normalizedHist = new double[histogram.length]; for (int i = 0; i < histogram.length; i++) { normalizedHist[i] = histogram[i] / sum; } // double[] pT = new double[histogram.length]; pT[0] = normalizedHist[0]; for (int i = 1; i < histogram.length; i++) { pT[i] = pT[i - 1] + normalizedHist[i]; } // Entropy for black and white parts of the histogram final double epsilon = Double.MIN_VALUE; double[] hB = new double[histogram.length]; double[] hW = new double[histogram.length]; for (int t = 0; t < histogram.length; t++) { // Black entropy if (pT[t] > epsilon) { double hhB = 0; for (int i = 0; i <= t; i++) { if (normalizedHist[i] > epsilon) { hhB -= normalizedHist[i] / pT[t] * Math.log(normalizedHist[i] / pT[t]); } } hB[t] = hhB; } else { hB[t] = 0; } // White entropy double pTW = 1 - pT[t]; if (pTW > epsilon) { double hhW = 0; for (int i = t + 1; i < histogram.length; ++i) { if (normalizedHist[i] > epsilon) { hhW -= normalizedHist[i] / pTW * Math.log(normalizedHist[i] / pTW); } } hW[t] = hhW; } else { hW[t] = 0; } } // Find histogram index with maximum entropy double jMax = hB[0] + hW[0]; int tMax = 0; for (int t = 1; t < histogram.length; ++t) { double j = hB[t] + hW[t]; if (j > jMax) { jMax = j; tMax = t; } } return tMax; }
private boolean watershedSegment(FastBitmap ip) { int width = ip.getWidth(); int height = ip.getHeight(); byte[] pixels = ip.getGrayData(); // Create an array with the coordinates of all points between value 1 and 254 // This method, suggested by Stein Roervik (stein_at_kjemi-dot-unit-dot-no), // greatly speeds up the watershed segmentation routine. ImageStatistics is = new ImageStatistics(ip); int[] histogram = is.getHistogramGray().getValues(); int arraySize = width * height - histogram[0] - histogram[255]; int[] coordinates = new int[arraySize]; // from pixel coordinates, low bits x, high bits y int highestValue = 0; int maxBinSize = 0; int offset = 0; int[] levelStart = new int[256]; for (int v = 1; v < 255; v++) { levelStart[v] = offset; offset += histogram[v]; if (histogram[v] > 0) highestValue = v; if (histogram[v] > maxBinSize) maxBinSize = histogram[v]; } int[] levelOffset = new int[highestValue + 1]; for (int y = 0, i = 0; y < height; y++) { for (int x = 0; x < width; x++, i++) { int v = pixels[i] & 255; if (v > 0 && v < 255) { offset = levelStart[v] + levelOffset[v]; coordinates[offset] = x | y << intEncodeShift; levelOffset[v]++; } } // for x } // for y // Create an array of the points (pixel offsets) that we set to 255 in one pass. // If we remember this list we need not create a snapshot of the ImageProcessor. int[] setPointList = new int[Math.min(maxBinSize, (width * height + 2) / 3)]; // now do the segmentation, starting at the highest level and working down. // At each level, dilate the particle (set pixels to 255), constrained to pixels // whose values are at that level and also constrained (by the fateTable) // to prevent features from merging. int[] table = makeFateTable(); final int[] directionSequence = new int[] {7, 3, 1, 5, 0, 4, 2, 6}; // diagonal directions first for (int level = highestValue; level >= 1; level--) { int remaining = histogram[level]; // number of points in the level that have not been processed int idle = 0; while (remaining > 0 && idle < 8) { int dIndex = 0; do { // expand each level in 8 directions int n = processLevel( directionSequence[dIndex % 8], ip, table, levelStart[level], remaining, coordinates, setPointList); // IJ.log("level="+level+" direction="+directionSequence[dIndex%8]+" // remain="+remaining+"-"+n); remaining -= n; // number of points processed if (n > 0) idle = 0; // nothing processed in this direction? dIndex++; } while (remaining > 0 && idle++ < 8); } if (remaining > 0 && level > 1) { // any pixels that we have not reached? int nextLevel = level; // find the next level to process do nextLevel--; while (nextLevel > 1 && histogram[nextLevel] == 0); // in principle we should add all unprocessed pixels of this level to the // tasklist of the next level. This would make it very slow for some images, // however. Thus we only add the pixels if they are at the border (of the // image or a thresholded area) and correct unprocessed pixels at the very // end by CleanupExtraLines if (nextLevel > 0) { int newNextLevelEnd = levelStart[nextLevel] + histogram[nextLevel]; for (int i = 0, p = levelStart[level]; i < remaining; i++, p++) { int xy = coordinates[p]; int x = xy & intEncodeXMask; int y = (xy & intEncodeYMask) >> intEncodeShift; int pOffset = x + y * width; boolean addToNext = false; if (x == 0 || y == 0 || x == width - 1 || y == height - 1) addToNext = true; // image border else for (int d = 0; d < 8; d++) if (isWithin(x, y, d, width, height) && pixels[pOffset + dirOffset[d]] == 0) { addToNext = true; // border of area below threshold break; } if (addToNext) coordinates[newNextLevelEnd++] = xy; } // tasklist for the next level to process becomes longer by this: histogram[nextLevel] = newNextLevelEnd - levelStart[nextLevel]; } } } return true; }