/** * Extract blob. * * @param id ID. * @param fastBitmap Image to be processed. * @return Image with the extracted blob. */ public FastBitmap Extract(int id, FastBitmap fastBitmap) { // Check if blobs list is null. if (this.blobs == null) this.blobs = new BlobDetection().ProcessImage(fastBitmap); FastBitmap image; if (fastBitmap.isGrayscale()) { image = new FastBitmap( fastBitmap.getWidth(), fastBitmap.getHeight(), FastBitmap.ColorSpace.Grayscale); for (IntPoint p : blobs.get(id).getPoints()) { image.setGray(p.x, p.y, fastBitmap.getGray(p.x, p.y)); } } else { image = new FastBitmap(fastBitmap.getWidth(), fastBitmap.getHeight(), FastBitmap.ColorSpace.RGB); for (IntPoint p : blobs.get(id).getPoints()) { image.setRed(p.x, p.y, fastBitmap.getRed(p.x, p.y)); image.setGreen(p.x, p.y, fastBitmap.getGreen(p.x, p.y)); image.setBlue(p.x, p.y, fastBitmap.getBlue(p.x, p.y)); } } return image; }
/** * Computes histograms. * * @param fastBitmap Image. */ private void ProcessImage(FastBitmap fastBitmap) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); if (fastBitmap.isGrayscale()) { int[] g = new int[width]; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { g[y] += fastBitmap.getGray(x, y); } } gray = new Histogram(g); } if (fastBitmap.isRGB()) { int[] r = new int[width]; int[] g = new int[width]; int[] b = new int[width]; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { r[y] += fastBitmap.getRed(x, y); g[y] += fastBitmap.getGreen(x, y); b[y] += fastBitmap.getBlue(x, y); } } red = new Histogram(r); green = new Histogram(g); blue = new Histogram(b); } }
@Override public void applyInPlace(FastBitmap fastBitmap) { if (fastBitmap.isRGB()) { int size = fastBitmap.getWidth() * fastBitmap.getHeight(); for (int i = 0; i < size; i++) { int r = fastBitmap.getRed(i); int g = fastBitmap.getGreen(i); int b = fastBitmap.getBlue(i); float[] color = ColorConverter.RGBtoHLS(r, g, b); int[] newColor = ColorConverter.HSLtoRGB(degree, color[1], color[2]); newColor[0] = newColor[0] > 255 ? 255 : newColor[0]; newColor[0] = newColor[0] < 0 ? 0 : newColor[0]; newColor[1] = newColor[1] > 255 ? 255 : newColor[1]; newColor[1] = newColor[1] < 0 ? 0 : newColor[1]; newColor[2] = newColor[2] > 255 ? 255 : newColor[2]; newColor[2] = newColor[2] < 0 ? 0 : newColor[2]; fastBitmap.setRGB(i, newColor); } } else { throw new IllegalArgumentException("Hue modifier only works in RGB images."); } }
@Override public void applyInPlace(FastBitmap fastBitmap) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); int noise = (width * height * noiseAmount) / 200; if (fastBitmap.isGrayscale()) { for (int i = 0; i < noise; i++) { int x = random.nextInt(height); int y = random.nextInt(width); int[] c = new int[] {0, 255}; int color = random.nextInt(2); fastBitmap.setGray(x, y, c[color]); } } else if (fastBitmap.isRGB()) { for (int i = 0; i < noise; i++) { int x = random.nextInt(height); int y = random.nextInt(width); int[] c = new int[] {0, 255}; int band = random.nextInt(2); int color = random.nextInt(2); if (band == 0) { fastBitmap.setRed(x, y, c[color]); } else if (band == 1) { fastBitmap.setGreen(x, y, c[color]); } else if (band == 2) { fastBitmap.setBlue(x, y, c[color]); } } } }
private static void watershedPostProcess(FastBitmap ip) { byte[] pixels = ip.getGrayData(); int size = ip.getWidth() * ip.getHeight(); for (int i = 0; i < size; i++) { if ((pixels[i] & 255) < 255) pixels[i] = (byte) 0; } }
private void CalculateNewSize(FastBitmap fastBitmap) { // return same size if original image size should be kept if (keepSize) { this.newWidth = fastBitmap.getWidth(); this.newHeight = fastBitmap.getHeight(); return; } // angle's sine and cosine double angleRad = -angle * Math.PI / 180; double angleCos = Math.cos(angleRad); double angleSin = Math.sin(angleRad); // calculate half size double halfWidth = (double) fastBitmap.getWidth() / 2; double halfHeight = (double) fastBitmap.getHeight() / 2; // rotate corners double cx1 = halfWidth * angleCos; double cy1 = halfWidth * angleSin; double cx2 = halfWidth * angleCos - halfHeight * angleSin; double cy2 = halfWidth * angleSin + halfHeight * angleCos; double cx3 = -halfHeight * angleSin; double cy3 = halfHeight * angleCos; double cx4 = 0; double cy4 = 0; // recalculate image size halfWidth = Math.max(Math.max(cx1, cx2), Math.max(cx3, cx4)) - Math.min(Math.min(cx1, cx2), Math.min(cx3, cx4)); halfHeight = Math.max(Math.max(cy1, cy2), Math.max(cy3, cy4)) - Math.min(Math.min(cy1, cy2), Math.min(cy3, cy4)); this.newWidth = (int) (halfWidth * 2 + 0.5); this.newHeight = (int) (halfHeight * 2 + 0.5); }
private void Watershed(FastBitmap fastBitmap) { DistanceTransform dt = new DistanceTransform(distance); float[][] distance = dt.Compute(fastBitmap); // Convert 2D to 1D - ImageJ Compatibility float[] distance1D = new float[distance.length * distance[0].length]; int p = 0; for (int i = 0; i < fastBitmap.getHeight(); i++) { for (int j = 0; j < fastBitmap.getWidth(); j++) { distance1D[p++] = distance[i][j]; } } // Make directions offsets makeDirectionOffsets(distance[0].length); int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); FastBitmap back = new FastBitmap(width, height, FastBitmap.ColorSpace.Grayscale); // Pegue as maxima long[] maxPoints = getSortedMaxPoints(distance, distance1D, back, 0, dt.getMaximumDistance(), -808080.0); // Analise e marque as maxima em imagem de background float maxSortingError = 1.1f * SQRT2 / 2f; analyseAndMarkMaxima(distance1D, back, maxPoints, tolerance, maxSortingError); // Transform em 8bit 0..255 FastBitmap outImage = make8Bit(distance, back, dt.getMaximumDistance(), -808080.0); cleanupMaxima(outImage, back, maxPoints); watershedSegment(outImage); watershedPostProcess(outImage); fastBitmap.setImage(outImage); }
@Override public void applyInPlace(FastBitmap sourceImage) { int width = overlayImage.getWidth(); int height = overlayImage.getHeight(); int[] rgbO; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { rgbO = overlayImage.getRGB(x, y); if (rgbO[0] >= _replaceColor[0] && rgbO[1] >= _replaceColor[1] && rgbO[2] >= _replaceColor[2]) { // x and y is reverted sourceImage.setRGB(x + getyOffset(), y + getxOffset(), rgbO); } } } }
@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; }
private int processLevel( int pass, FastBitmap ip, int[] fateTable, int levelStart, int levelNPoints, int[] coordinates, int[] setPointList) { int width = ip.getWidth(); int height = ip.getHeight(); int xmax = width - 1; int ymax = height - 1; byte[] pixels = ip.getGrayData(); int nChanged = 0; int nUnchanged = 0; for (int i = 0, p = levelStart; i < levelNPoints; i++, p++) { int xy = coordinates[p]; int x = xy & intEncodeXMask; int y = (xy & intEncodeYMask) >> intEncodeShift; int offset = x + y * width; int index = 0; // neighborhood pixel ocupation: index in fateTable if (y > 0 && (pixels[offset - width] & 255) == 255) index ^= 1; if (x < xmax && y > 0 && (pixels[offset - width + 1] & 255) == 255) index ^= 2; if (x < xmax && (pixels[offset + 1] & 255) == 255) index ^= 4; if (x < xmax && y < ymax && (pixels[offset + width + 1] & 255) == 255) index ^= 8; if (y < ymax && (pixels[offset + width] & 255) == 255) index ^= 16; if (x > 0 && y < ymax && (pixels[offset + width - 1] & 255) == 255) index ^= 32; if (x > 0 && (pixels[offset - 1] & 255) == 255) index ^= 64; if (x > 0 && y > 0 && (pixels[offset - width - 1] & 255) == 255) index ^= 128; int mask = 1 << pass; if ((fateTable[index] & mask) == mask) setPointList[nChanged++] = offset; // remember to set pixel to 255 else coordinates[levelStart + (nUnchanged++)] = xy; // keep this pixel for future passes } // for pixel i for (int i = 0; i < nChanged; i++) pixels[setPointList[i]] = (byte) 255; return nChanged; }
@Override public void applyInPlace(FastBitmap fastBitmap) { if (fastBitmap.isRGB() && overlay.isRGB()) { int w = fastBitmap.getWidth(); int h = fastBitmap.getHeight(); switch (algorithm) { case Lighten: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (overlay.getRed(i, j) > fastBitmap.getRed(i, j)) { fastBitmap.setRed(i, j, overlay.getRed(i, j)); } if (overlay.getGreen(i, j) > fastBitmap.getGreen(i, j)) { fastBitmap.setGreen(i, j, overlay.getGreen(i, j)); } if (overlay.getBlue(i, j) > fastBitmap.getBlue(i, j)) { fastBitmap.setBlue(i, j, overlay.getBlue(i, j)); } } } break; case Darken: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (overlay.getRed(i, j) < fastBitmap.getRed(i, j)) { fastBitmap.setRed(i, j, overlay.getRed(i, j)); } if (overlay.getGreen(i, j) < fastBitmap.getGreen(i, j)) { fastBitmap.setGreen(i, j, overlay.getGreen(i, j)); } if (overlay.getBlue(i, j) < fastBitmap.getBlue(i, j)) { fastBitmap.setBlue(i, j, overlay.getBlue(i, j)); } } } break; case Multiply: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255; int g = fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255; int b = fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255; fastBitmap.setRGB(i, j, r, g, b); } } break; case Average: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = fastBitmap.getRed(i, j) + overlay.getRed(i, j) / 2; int g = fastBitmap.getGreen(i, j) + overlay.getGreen(i, j) / 2; int b = fastBitmap.getBlue(i, j) + overlay.getBlue(i, j) / 2; fastBitmap.setRGB(i, j, r, g, b); } } break; case Add: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = Math.min(fastBitmap.getRed(i, j) + overlay.getRed(i, j), 255); int g = Math.min(fastBitmap.getGreen(i, j) + overlay.getGreen(i, j), 255); int b = Math.min(fastBitmap.getBlue(i, j) + overlay.getBlue(i, j), 255); fastBitmap.setRGB(i, j, r, g, b); } } break; case Subtract: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int temp = fastBitmap.getRed(i, j) + overlay.getRed(i, j); if (temp < 255) { fastBitmap.setRed(i, j, 0); } else { fastBitmap.setRed(i, j, temp - 255); } temp = fastBitmap.getGreen(i, j) + overlay.getGreen(i, j); if (temp < 255) { fastBitmap.setGreen(i, j, 0); } else { fastBitmap.setGreen(i, j, temp - 255); } temp = fastBitmap.getBlue(i, j) + overlay.getBlue(i, j); if (temp < 255) { fastBitmap.setBlue(i, j, 0); } else { fastBitmap.setBlue(i, j, temp - 255); } } } break; case Difference: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = Math.abs(fastBitmap.getRed(i, j) - overlay.getRed(i, j)); int g = Math.abs(fastBitmap.getGreen(i, j) - overlay.getGreen(i, j)); int b = Math.abs(fastBitmap.getBlue(i, j) - overlay.getBlue(i, j)); fastBitmap.setRGB(i, j, r, g, b); } } break; case Negation: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = 255 - Math.abs(255 - fastBitmap.getRed(i, j) - overlay.getRed(i, j)); int g = 255 - Math.abs(255 - fastBitmap.getGreen(i, j) - overlay.getGreen(i, j)); int b = 255 - Math.abs(255 - fastBitmap.getBlue(i, j) - overlay.getBlue(i, j)); fastBitmap.setRGB(i, j, r, g, b); } } break; case Screen: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = ((255 - (((255 - fastBitmap.getRed(i, j)) * (255 - overlay.getRed(i, j))) >> 8))); int g = ((255 - (((255 - fastBitmap.getGreen(i, j)) * (255 - overlay.getGreen(i, j))) >> 8))); int b = ((255 - (((255 - fastBitmap.getBlue(i, j)) * (255 - overlay.getBlue(i, j))) >> 8))); fastBitmap.setRGB(i, j, r, g, b); } } break; case Exclusion: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = ((fastBitmap.getRed(i, j) + overlay.getRed(i, j) - 2 * fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255)); int g = ((fastBitmap.getGreen(i, j) + overlay.getGreen(i, j) - 2 * fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255)); int b = ((fastBitmap.getBlue(i, j) + overlay.getBlue(i, j) - 2 * fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255)); fastBitmap.setRGB(i, j, r, g, b); } } break; case Overlay: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int temp; if (overlay.getRed(i, j) < 128) { temp = (2 * fastBitmap.getRed(i, j) * overlay.getRed(i, j) / 255); temp = Math.min(255, temp); fastBitmap.setRed(i, j, temp); } else { temp = (255 - 2 * (255 - fastBitmap.getRed(i, j)) * (255 - overlay.getRed(i, j)) / 255); temp = Math.min(255, temp); fastBitmap.setRed(i, j, temp); } if (overlay.getGreen(i, j) < 128) { temp = (2 * fastBitmap.getGreen(i, j) * overlay.getGreen(i, j) / 255); temp = Math.min(255, temp); fastBitmap.setGreen(i, j, temp); } else { temp = (255 - 2 * (255 - fastBitmap.getGreen(i, j)) * (255 - overlay.getGreen(i, j)) / 255); temp = Math.min(255, temp); fastBitmap.setGreen(i, j, temp); } if (overlay.getBlue(i, j) < 128) { temp = (2 * fastBitmap.getBlue(i, j) * overlay.getBlue(i, j) / 255); temp = Math.min(255, temp); fastBitmap.setBlue(i, j, temp); } else { temp = (255 - 2 * (255 - fastBitmap.getBlue(i, j)) * (255 - overlay.getBlue(i, j)) / 255); temp = Math.min(255, temp); fastBitmap.setBlue(i, j, temp); } } } break; case SoftLight: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int temp; if (fastBitmap.getRed(i, j) < 128) { temp = (2 * overlay.getRed(i, j) * fastBitmap.getRed(i, j) / 255); temp = Math.min(255, temp); fastBitmap.setRed(i, j, temp); } else { temp = (255 - 2 * (255 - overlay.getRed(i, j)) * (255 - fastBitmap.getRed(i, j)) / 255); temp = Math.min(255, temp); overlay.setRed(i, j, temp); } if (fastBitmap.getGreen(i, j) < 128) { temp = (2 * overlay.getGreen(i, j) * fastBitmap.getGreen(i, j) / 255); temp = Math.min(255, temp); fastBitmap.setGreen(i, j, temp); } else { temp = (255 - 2 * (255 - overlay.getGreen(i, j)) * (255 - fastBitmap.getGreen(i, j)) / 255); temp = Math.min(255, temp); fastBitmap.setGreen(i, j, temp); } if (fastBitmap.getBlue(i, j) < 128) { temp = (2 * overlay.getBlue(i, j) * fastBitmap.getBlue(i, j) / 255); temp = Math.min(255, temp); fastBitmap.setBlue(i, j, temp); } else { temp = (255 - 2 * (255 - overlay.getBlue(i, j)) * (255 - fastBitmap.getBlue(i, j)) / 255); temp = Math.min(255, temp); fastBitmap.setBlue(i, j, temp); } } } break; case HardLight: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { float temp; if (overlay.getRed(i, j) < 128) { temp = (2 * ((fastBitmap.getRed(i, j) >> 1) + 64)) * ((float) overlay.getRed(i, j) / 255); fastBitmap.setRed(i, j, (int) temp); } else { temp = (255 - (2 * (255 - ((fastBitmap.getRed(i, j) >> 1) + 64)) * (float) (255 - overlay.getRed(i, j)) / 255)); fastBitmap.setRed(i, j, (int) temp); } if (overlay.getGreen(i, j) < 128) { temp = (2 * ((fastBitmap.getGreen(i, j) >> 1) + 64)) * ((float) overlay.getGreen(i, j) / 255); fastBitmap.setGreen(i, j, (int) temp); } else { temp = (255 - (2 * (255 - ((fastBitmap.getGreen(i, j) >> 1) + 64)) * (float) (255 - overlay.getGreen(i, j)) / 255)); fastBitmap.setGreen(i, j, (int) temp); } if (overlay.getBlue(i, j) < 128) { temp = (2 * ((fastBitmap.getBlue(i, j) >> 1) + 64)) * ((float) overlay.getBlue(i, j) / 255); fastBitmap.setBlue(i, j, (int) temp); } else { temp = (255 - (2 * (255 - ((fastBitmap.getBlue(i, j) >> 1) + 64)) * (float) (255 - overlay.getBlue(i, j)) / 255)); fastBitmap.setBlue(i, j, (int) temp); } } } break; case ColorDodge: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (overlay.getRed(i, j) == 255) { fastBitmap.setRed(i, j, 255); } else { int x = Math.min(255, ((fastBitmap.getRed(i, j) << 8) / (255 - overlay.getRed(i, j)))); fastBitmap.setRed(i, j, x); } if (overlay.getGreen(i, j) == 255) { fastBitmap.setGreen(i, j, 255); } else { int x = Math.min( 255, ((fastBitmap.getGreen(i, j) << 8) / (255 - overlay.getGreen(i, j)))); fastBitmap.setGreen(i, j, x); } if (overlay.getBlue(i, j) == 255) { fastBitmap.setBlue(i, j, 255); } else { int x = Math.min( 255, ((fastBitmap.getBlue(i, j) << 8) / (255 - overlay.getBlue(i, j)))); fastBitmap.setBlue(i, j, x); } } } break; case ColorBurn: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (overlay.getRed(i, j) == 0) { fastBitmap.setRed(i, j, overlay.getRed(i, j)); } else { int x = Math.max( 0, (255 - ((255 - fastBitmap.getRed(i, j)) << 8) / overlay.getRed(i, j))); fastBitmap.setRed(i, j, x); } if (overlay.getGreen(i, j) == 0) { fastBitmap.setGreen(i, j, overlay.getGreen(i, j)); } else { int x = Math.max( 0, (255 - ((255 - fastBitmap.getGreen(i, j)) << 8) / overlay.getGreen(i, j))); fastBitmap.setGreen(i, j, x); } if (overlay.getBlue(i, j) == 0) { fastBitmap.setBlue(i, j, overlay.getBlue(i, j)); } else { int x = Math.max( 0, (255 - ((255 - fastBitmap.getBlue(i, j)) << 8) / overlay.getBlue(i, j))); fastBitmap.setBlue(i, j, x); } } } break; case LinearLight: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int temp; if (overlay.getRed(i, j) < 128) { temp = fastBitmap.getRed(i, j) + (2 * overlay.getRed(i, j)); if (temp < 255) { fastBitmap.setRed(i, j, 0); } else { fastBitmap.setRed(i, j, (temp - 255)); } } else { int x = Math.min(fastBitmap.getRed(i, j) + (2 * (overlay.getRed(i, j) - 128)), 255); fastBitmap.setRed(i, j, x); } if (overlay.getGreen(i, j) < 128) { temp = fastBitmap.getGreen(i, j) + (2 * overlay.getGreen(i, j)); if (temp < 255) { fastBitmap.setGreen(i, j, 0); } else { fastBitmap.setGreen(i, j, (temp - 255)); } } else { int x = Math.min(fastBitmap.getGreen(i, j) + (2 * (overlay.getGreen(i, j) - 128)), 255); fastBitmap.setGreen(i, j, x); } if (overlay.getBlue(i, j) < 128) { temp = fastBitmap.getBlue(i, j) + (2 * overlay.getBlue(i, j)); if (temp < 255) { fastBitmap.setBlue(i, j, 0); } else { fastBitmap.setBlue(i, j, (temp - 255)); } } else { int x = Math.min(fastBitmap.getBlue(i, j) + (2 * (overlay.getBlue(i, j) - 128)), 255); fastBitmap.setBlue(i, j, x); } } } break; case VividLight: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (overlay.getRed(i, j) < 128) { // Color Burn int o = overlay.getRed(i, j) * 2; if (o == 0) { fastBitmap.setRed(i, j, o); } else { int x = Math.max(0, (255 - ((255 - fastBitmap.getRed(i, j)) << 8) / o)); fastBitmap.setRed(i, j, x); } } else { // Color Dodge int o = 2 * (overlay.getRed(i, j) - 128); if (o == 255) { fastBitmap.setRed(i, j, 255); } else { int x = Math.min(255, ((fastBitmap.getRed(i, j) << 8) / (255 - o))); fastBitmap.setRed(i, j, x); } } if (overlay.getGreen(i, j) < 128) { // Color Burn int o = overlay.getGreen(i, j) * 2; if (o == 0) { fastBitmap.setGreen(i, j, o); } else { int x = Math.max(0, (255 - ((255 - fastBitmap.getGreen(i, j)) << 8) / o)); fastBitmap.setGreen(i, j, x); } } else { // Color Dodge int o = 2 * (overlay.getGreen(i, j) - 128); if (o == 255) { fastBitmap.setGreen(i, j, 255); } else { int x = Math.min(255, ((fastBitmap.getGreen(i, j) << 8) / (255 - o))); fastBitmap.setGreen(i, j, x); } } if (overlay.getBlue(i, j) < 128) { // Color Burn int o = overlay.getBlue(i, j) * 2; if (o == 0) { fastBitmap.setBlue(i, j, o); } else { int x = Math.max(0, (255 - ((255 - fastBitmap.getBlue(i, j)) << 8) / o)); fastBitmap.setBlue(i, j, x); } } else { // Color Dodge int o = 2 * (overlay.getBlue(i, j) - 128); if (o == 255) { fastBitmap.setGreen(i, j, 255); } else { int x = Math.min(255, ((fastBitmap.getBlue(i, j) << 8) / (255 - o))); fastBitmap.setBlue(i, j, x); } } } } break; case PinLight: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int o = overlay.getRed(i, j) * 2; if (overlay.getRed(i, j) < 128) { // Darken if (o < fastBitmap.getRed(i, j)) { fastBitmap.setRed(i, j, o); } } else { // Lighten if (o > fastBitmap.getRed(i, j)) { fastBitmap.setRed(i, j, o); } } o = overlay.getGreen(i, j) * 2; if (overlay.getGreen(i, j) < 128) { // Darken if (o < fastBitmap.getGreen(i, j)) { fastBitmap.setGreen(i, j, o); } } else { // Lighten if (o > fastBitmap.getGreen(i, j)) { fastBitmap.setGreen(i, j, o); } } o = overlay.getBlue(i, j) * 2; if (overlay.getBlue(i, j) < 128) { // Darken if (o < fastBitmap.getBlue(i, j)) { fastBitmap.setBlue(i, j, o); } } else { // Lighten if (o > fastBitmap.getBlue(i, j)) { fastBitmap.setBlue(i, j, o); } } } } break; case Reflect: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (overlay.getRed(i, j) == 255) { fastBitmap.setRed(i, j, 255); } else { int x = Math.min( 255, (fastBitmap.getRed(i, j) * fastBitmap.getRed(i, j) / (255 - overlay.getRed(i, j)))); fastBitmap.setRed(i, j, x); } if (overlay.getGreen(i, j) == 255) { fastBitmap.setGreen(i, j, 255); } else { int x = Math.min( 255, (fastBitmap.getGreen(i, j) * fastBitmap.getGreen(i, j) / (255 - overlay.getGreen(i, j)))); fastBitmap.setGreen(i, j, x); } if (overlay.getBlue(i, j) == 255) { fastBitmap.setBlue(i, j, 255); } else { int x = Math.min( 255, (fastBitmap.getBlue(i, j) * fastBitmap.getBlue(i, j) / (255 - overlay.getBlue(i, j)))); fastBitmap.setBlue(i, j, x); } } } break; case Phoenix: for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int r = ((Math.min(fastBitmap.getRed(i, j), overlay.getRed(i, j)) - Math.max(fastBitmap.getRed(i, j), overlay.getRed(i, j)) + 255)); int g = ((Math.min(fastBitmap.getGreen(i, j), overlay.getGreen(i, j)) - Math.max(fastBitmap.getGreen(i, j), overlay.getGreen(i, j)) + 255)); int b = ((Math.min(fastBitmap.getBlue(i, j), overlay.getBlue(i, j)) - Math.max(fastBitmap.getBlue(i, j), overlay.getBlue(i, j)) + 255)); fastBitmap.setRGB(i, j, r, g, b); } } break; } } else { throw new IllegalArgumentException("Blend only works in RGB images."); } }
@Override public void applyInPlace(FastBitmap fastBitmap) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); FastBitmap copy = new FastBitmap(fastBitmap); if (fastBitmap.isGrayscale()) { for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { double mean = 0; double var = 0; int total = 0; for (int i = x - radius; i <= x + radius; i++) { for (int j = y - radius; j <= y + radius; j++) { if (i >= 0 && i < height && j >= 0 && j < width) { mean += copy.getGray(i, j); total++; } } } mean /= total; for (int i = x - radius; i <= x + radius; i++) { for (int j = y - radius; j <= y + radius; j++) { if (i >= 0 && i < height && j >= 0 && j < width) var += Math.pow(copy.getGray(i, j) - mean, 2); } } var /= total - 1; if (var < 0) var = 0; if (var > 255) var = 255; fastBitmap.setGray(x, y, (int) var); } } } if (fastBitmap.isRGB()) { for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { double meanR = 0, meanG = 0, meanB = 0; double varR = 0, varG = 0, varB = 0; int total = 0; for (int i = x - radius; i <= x + radius; i++) { for (int j = y - radius; j <= y + radius; j++) { if (i >= 0 && i < height && j >= 0 && j < width) { meanR += copy.getRed(i, j); meanG += copy.getGreen(i, j); meanB += copy.getBlue(i, j); total++; } } } meanR /= total; meanG /= total; meanB /= total; for (int i = x - radius; i <= x + radius; i++) { for (int j = y - radius; j <= y + radius; j++) { if (i >= 0 && i < height && j >= 0 && j < width) { varR += Math.pow(copy.getRed(i, j) - meanR, 2); varG += Math.pow(copy.getGreen(i, j) - meanG, 2); varB += Math.pow(copy.getBlue(i, j) - meanB, 2); } } } varR /= total - 1; varG /= total - 1; varB /= total - 1; if (varR < 0) varR = 0; if (varG < 0) varG = 0; if (varB < 0) varB = 0; if (varR > 255) varR = 255; if (varG > 255) varG = 255; if (varB > 255) varB = 255; fastBitmap.setRGB(x, y, (int) varR, (int) varG, (int) varB); } } } }
@Override public void applyInPlace(FastBitmap fastBitmap) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); int Xline, Yline; int radiusI = (weight.length - 1) / 2; int radiusJ = (weight[0].length - 1) / 2; int maxArray = calcMax(weight); int c; FastBitmap copy = new FastBitmap(fastBitmap); if (fastBitmap.isGrayscale()) { int[] avgL = new int[maxArray]; int median; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { c = 0; for (int i = 0; i < weight.length; i++) { Xline = x + (i - radiusI); for (int j = 0; j < weight[0].length; j++) { Yline = y + (j - radiusJ); if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) { if (weight[i][j] > 0) { for (int k = 0; k < weight[i][j]; k++) { avgL[c] = copy.getGray(Xline, Yline); c++; } } } } } Arrays.sort(avgL, 0, c); // median median = c / 2; fastBitmap.setGray(x, y, avgL[median]); } } } else if (fastBitmap.isRGB()) { int[] avgR = new int[maxArray]; int[] avgG = new int[maxArray]; int[] avgB = new int[maxArray]; int median; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { c = 0; for (int i = 0; i < weight.length; i++) { Xline = x + (i - radiusI); for (int j = 0; j < weight[0].length; j++) { Yline = y + (j - radiusJ); if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) { if (weight[i][j] > 0) { for (int k = 0; k < weight[i][j]; k++) { avgR[c] = copy.getRed(Xline, Yline); avgG[c] = copy.getGreen(Xline, Yline); avgB[c] = copy.getBlue(Xline, Yline); c++; } } } } } Arrays.sort(avgR, 0, c); Arrays.sort(avgG, 0, c); Arrays.sort(avgB, 0, c); // median median = c / 2; fastBitmap.setRGB(x, y, avgR[median], avgG[median], avgB[median]); } } } }
@Override public void applyInPlace(FastBitmap fastBitmap) { if (fastBitmap.isGrayscale()) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); double oldIradius = (double) (height - 1) / 2; double oldJradius = (double) (width - 1) / 2; CalculateNewSize(fastBitmap); FastBitmap destinationData = new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.Grayscale); // get destination image size double newIradius = (double) (newHeight - 1) / 2; double newJradius = (double) (newWidth - 1) / 2; // angle's sine and cosine double angleRad = -angle * Math.PI / 180; double angleCos = Math.cos(angleRad); double angleSin = Math.sin(angleRad); // destination pixel's coordinate relative to image center double ci, cj; // coordinates of source points and cooefficiens double oi, oj, di, dj, k1, k2; int oi1, oj1, oi2, oj2; // width and height decreased by 1 int imax = height - 1; int jmax = width - 1; ci = -newIradius; for (int i = 0; i < newHeight; i++) { cj = -newJradius; for (int j = 0; j < newWidth; j++) { // coordinate of the nearest point oi = angleCos * ci + angleSin * cj + oldIradius; oj = -angleSin * ci + angleCos * cj + oldJradius; oi1 = (int) oi; oj1 = (int) oj; // validate source pixel's coordinates if ((oi1 < 0) || (oj1 < 0) || (oi1 >= height) || (oj1 >= width)) { // fill destination image with filler destinationData.setGray(i, j, fillGray); } else { di = oi - (double) oi1; dj = oj - (double) oj1; // initial pixel value int g = 0; for (int n = -1; n < 3; n++) { // get Y cooefficient k1 = Interpolation.BiCubicKernel(dj - (double) n); oj2 = oj1 + n; if (oj2 < 0) oj2 = 0; if (oj2 > jmax) oj2 = jmax; for (int m = -1; m < 3; m++) { // get X cooefficient k2 = k1 * Interpolation.BiCubicKernel((double) m - di); oi2 = oi1 + m; if (oi2 < 0) oi2 = 0; if (oi2 > imax) oi2 = imax; g += k2 * fastBitmap.getGray(oi2, oj2); } } destinationData.setGray(i, j, Math.max(0, Math.min(255, g))); } cj++; } ci++; } fastBitmap.setImage(destinationData); destinationData.recycle(); } else if (fastBitmap.isRGB()) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); double oldIradius = (double) (height - 1) / 2; double oldJradius = (double) (width - 1) / 2; CalculateNewSize(fastBitmap); FastBitmap destinationData = new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.RGB); // get destination image size double newIradius = (double) (newHeight - 1) / 2; double newJradius = (double) (newWidth - 1) / 2; // angle's sine and cosine double angleRad = -angle * Math.PI / 180; double angleCos = Math.cos(angleRad); double angleSin = Math.sin(angleRad); // destination pixel's coordinate relative to image center double ci, cj; // coordinates of source points and cooefficiens double oi, oj, di, dj, k1, k2; int oi1, oj1, oi2, oj2; // width and height decreased by 1 int imax = height - 1; int jmax = width - 1; ci = -newIradius; for (int i = 0; i < newHeight; i++) { cj = -newJradius; for (int j = 0; j < newWidth; j++) { // coordinate of the nearest point oi = angleCos * ci + angleSin * cj + oldIradius; oj = -angleSin * ci + angleCos * cj + oldJradius; oi1 = (int) oi; oj1 = (int) oj; // validate source pixel's coordinates if ((oi < 0) || (oj < 0) || (oi >= height) || (oj >= width)) { // fill destination image with filler destinationData.setRGB(i, j, fillRed, fillGreen, fillBlue); } else { di = oi - (double) oi1; dj = oj - (double) oj1; // initial pixel value int r = 0; int g = 0; int b = 0; for (int n = -1; n < 3; n++) { // get Y cooefficient k1 = Interpolation.BiCubicKernel(dj - (double) n); oj2 = oj1 + n; if (oj2 < 0) oj2 = 0; if (oj2 > jmax) oj2 = jmax; for (int m = -1; m < 3; m++) { // get X cooefficient k2 = k1 * Interpolation.BiCubicKernel((double) m - di); oi2 = oi1 + m; if (oi2 < 0) oi2 = 0; if (oi2 > imax) oi2 = imax; r += k2 * fastBitmap.getRed(oi2, oj2); g += k2 * fastBitmap.getGreen(oi2, oj2); b += k2 * fastBitmap.getBlue(oi2, oj2); } } r = Math.max(0, Math.min(255, r)); g = Math.max(0, Math.min(255, g)); b = Math.max(0, Math.min(255, b)); destinationData.setRGB(i, j, r, g, b); } cj++; } ci++; } fastBitmap.setImage(destinationData); destinationData.recycle(); } }
@Override public void applyInPlace(FastBitmap fastBitmap) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); int Xline, Yline; int lines = CalcLines(radius); int maxArray = lines * lines; int c; FastBitmap copy = new FastBitmap(fastBitmap); if (fastBitmap.isGrayscale()) { int[] avgL = new int[maxArray]; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { c = 0; for (int i = 0; i < lines; i++) { Xline = x + (i - radius); for (int j = 0; j < lines; j++) { Yline = y + (j - radius); if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) { avgL[c] = copy.getGray(Xline, Yline); } else { avgL[c] = copy.getGray(x, y); } c++; } } Arrays.sort(avgL); // alpha trimmed mean double mean = 0; for (int i = t; i < c - t; i++) { mean += avgL[i]; } fastBitmap.setGray(x, y, (int) (mean / (avgL.length - 2 * t))); } } } else if (fastBitmap.isRGB()) { int[] avgR = new int[maxArray]; int[] avgG = new int[maxArray]; int[] avgB = new int[maxArray]; for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { c = 0; for (int i = 0; i < lines; i++) { Xline = x + (i - radius); for (int j = 0; j < lines; j++) { Yline = y + (j - radius); if ((Xline >= 0) && (Xline < height) && (Yline >= 0) && (Yline < width)) { avgR[c] = copy.getRed(Xline, Yline); avgG[c] = copy.getGreen(Xline, Yline); avgB[c] = copy.getBlue(Xline, Yline); } else { avgR[c] = copy.getRed(x, y); avgG[c] = copy.getGreen(x, y); avgB[c] = copy.getBlue(x, y); } c++; } } Arrays.sort(avgR); Arrays.sort(avgG); Arrays.sort(avgB); // alpha trimmed mean double meanR = 0, meanG = 0, meanB = 0; for (int i = t; i < c - t; i++) { meanR += avgR[i]; meanG += avgG[i]; meanB += avgB[i]; } meanR /= (avgR.length - 2 * t); meanG /= (avgG.length - 2 * t); meanB /= (avgB.length - 2 * t); fastBitmap.setRGB(x, y, (int) meanR, (int) meanG, (int) meanB); } } } }
private void analyseAndMarkMaxima( float[] edmPixels, FastBitmap back, long[] maxPoints, float tolerance, float maxSortingError) { int width = back.getWidth(); int height = back.getHeight(); byte[] types = (byte[]) back.getGrayData(); int nMax = maxPoints.length; int[] pList = new int[width * height]; // here we enter points starting from a maximum for (int iMax = nMax - 1; iMax >= 0; iMax--) { // process all maxima now, starting from the highest int offset0 = (int) maxPoints[iMax]; // type cast gets 32 lower bits, where pixel index is encoded // int offset0 = maxPoints[iMax].offset; if ((types[offset0] & (byte) 4) != 0) // this maximum has been reached from another one, skip it continue; // we create a list of connected points and start the list at the current maximum int x0 = offset0 % width; int y0 = offset0 / width; float v0 = trueEdmHeight(x0, y0, edmPixels, width, height); boolean sortingError; do { // repeat if we have encountered a sortingError pList[0] = offset0; types[offset0] |= ((byte) 16 | (byte) 2); // mark first point as equal height (to itself) and listed int listLen = 1; // number of elements in the list int listI = 0; // index of current element in the list sortingError = false; // if sorting was inaccurate: a higher maximum was not handled so far boolean maxPossible = true; // it may be a true maximum double xEqual = x0; // for creating a single point: determine average over the double yEqual = y0; // coordinates of contiguous equal-height points int nEqual = 1; // counts xEqual/yEqual points that we use for averaging do { // while neigbor list is not fully processed (to listLen) int offset = pList[listI]; int x = offset % width; int y = offset / width; boolean isInner = (y != 0 && y != height - 1) && (x != 0 && x != width - 1); // not necessary, but faster than isWithin for (int d = 0; d < 8; d++) { // analyze all neighbors (in 8 directions) at the same level int offset2 = offset + dirOffset[d]; if ((isInner || isWithin(x, y, d, width, height)) && (types[offset2] & (byte) 2) == 0) { if (edmPixels[offset2] <= 0) continue; // ignore the background (non-particles) if ((types[offset2] & (byte) 4) != 0) { maxPossible = false; // we have reached a point processed previously, thus it is no maximum // now break; } int x2 = x + DIR_X_OFFSET[d]; int y2 = y + DIR_Y_OFFSET[d]; float v2 = trueEdmHeight(x2, y2, edmPixels, width, height); if (v2 > v0 + maxSortingError) { maxPossible = false; // we have reached a higher point, thus it is no maximum // if(x0<25&&y0<20)IJ.write("x0,y0="+x0+","+y0+":stop at higher neighbor from // x,y="+x+","+y+", dir="+d+",value,value2,v2-v="+v0+","+v2+","+(v2-v0)); break; } else if (v2 >= v0 - (float) tolerance) { if (v2 > v0) { // maybe this point should have been treated earlier sortingError = true; offset0 = offset2; v0 = v2; x0 = x2; y0 = y2; } pList[listLen] = offset2; listLen++; // we have found a new point within the tolerance types[offset2] |= (byte) 2; if (v2 == v0) { // prepare finding center of equal points (in case single point needed) types[offset2] |= (byte) 16; xEqual += x2; yEqual += y2; nEqual++; } } } // if isWithin & not (byte)2 } // for directions d listI++; } while (listI < listLen); if (sortingError) { // if x0,y0 was not the true maximum but we have reached a higher one for (listI = 0; listI < listLen; listI++) types[pList[listI]] = 0; // reset all points encountered, then retry } else { int resetMask = ~(maxPossible ? (byte) 2 : ((byte) 2 | (byte) 16)); xEqual /= nEqual; yEqual /= nEqual; double minDist2 = 1e20; int nearestI = 0; for (listI = 0; listI < listLen; listI++) { int offset = pList[listI]; int x = offset % width; int y = offset / width; types[offset] &= resetMask; // reset attributes no longer needed types[offset] |= (byte) 4; // mark as processed if (maxPossible) { types[offset] |= (byte) 8; if ((types[offset] & (byte) 16) != 0) { double dist2 = (xEqual - x) * (double) (xEqual - x) + (yEqual - y) * (double) (yEqual - y); if (dist2 < minDist2) { minDist2 = dist2; // this could be the best "single maximum" point nearestI = listI; } } } } // for listI if (maxPossible) { int offset = pList[nearestI]; types[offset] |= (byte) 32; } } // if !sortingError } while (sortingError); // redo if we have encountered a higher maximum: handle it now. } // for all maxima iMax }
/** * Compute GLRLM. * * @param fastBitmap Image to be processed. * @return GLRLM. */ public double[][] Compute(FastBitmap fastBitmap) { int maxGray = 255; if (autoGray) maxGray = ImageStatistics.Maximum(fastBitmap); int height = fastBitmap.getHeight(); int width = fastBitmap.getWidth(); double[][] runMatrix = new double[maxGray + 1][width + 1]; switch (degree) { case Degree_0: for (int i = 0; i < height; i++) { int runs = 1; for (int j = 1; j < width; j++) { int g1 = fastBitmap.getGray(i, j - 1); int g2 = fastBitmap.getGray(i, j); if (g1 == g2) { runs++; } else { runMatrix[g1][runs]++; numPrimitives++; runs = 1; } if ((g1 == g2) && (j == width - 1)) { runMatrix[g1][runs]++; } if ((g1 != g2) && (j == width - 1)) { runMatrix[g2][1]++; } } } break; case Degree_45: // Compute I(0,0) and I(height,width) runMatrix[0][1]++; runMatrix[height - 1][width - 1]++; // Compute height for (int i = 1; i < height; i++) { int runs = 1; int steps = i; for (int j = 0; j < steps; j++) { int g1 = fastBitmap.getGray(i - j, j); int g2 = fastBitmap.getGray(i - j - 1, j + 1); if (g1 == g2) { runs++; } else { runMatrix[g1][runs]++; numPrimitives++; runs = 1; } if ((g1 == g2) && (j == steps - 1)) { runMatrix[g1][runs]++; } if ((g1 != g2) && (j == steps - 1)) { runMatrix[g2][1]++; } } } // Compute width for (int j = 1; j < width - 1; j++) { int runs = 1; int steps = height - j; for (int i = 1; i < steps; i++) { int g1 = fastBitmap.getGray(height - i, j + i - 1); int g2 = fastBitmap.getGray(height - i - 1, j + i); if (g1 == g2) { runs++; } else { runMatrix[g1][runs]++; numPrimitives++; runs = 1; } if ((g1 == g2) && (i == steps - 1)) { runMatrix[g1][runs]++; } if ((g1 != g2) && (i == steps - 1)) { runMatrix[g2][1]++; } } } break; case Degree_90: for (int j = 0; j < width; j++) { int runs = 1; for (int i = 0; i < height - 1; i++) { int g1 = fastBitmap.getGray(height - i - 1, j); int g2 = fastBitmap.getGray(height - i - 2, j); if (g1 == g2) { runs++; } else { runMatrix[g1][runs]++; numPrimitives++; runs = 1; } if ((g1 == g2) && (i == height - 2)) { runMatrix[g1][runs]++; } if ((g1 != g2) && (i == height - 2)) { runMatrix[g2][1]++; } } } break; case Degree_135: // Compute I(0,width) and I(height,0) runMatrix[0][width - 1]++; runMatrix[height - 1][0]++; // Compute height for (int i = 1; i < width; i++) { int runs = 1; int steps = i; int w = width - 1; for (int j = 0; j < steps; j++) { int g1 = fastBitmap.getGray(i - j, w); int g2 = fastBitmap.getGray(i - j - 1, --w); if (g1 == g2) { runs++; } else { runMatrix[g1][runs]++; numPrimitives++; runs = 1; } if ((g1 == g2) && (j == steps - 1)) { runMatrix[g1][runs]++; } if ((g1 != g2) && (j == steps - 1)) { runMatrix[g2][1]++; } } } // Compute width for (int j = 1; j < width - 1; j++) { int runs = 1; int steps = height - j; int w = width - 1 - j; for (int i = 1; i < steps; i++) { int g1 = fastBitmap.getGray(height - i, w); int g2 = fastBitmap.getGray(height - i - 1, --w); if (g1 == g2) { runs++; } else { runMatrix[g1][runs]++; numPrimitives++; runs = 1; } if ((g1 == g2) && (i == steps - 1)) { runMatrix[g1][runs]++; } if ((g1 != g2) && (i == steps - 1)) { runMatrix[g2][1]++; } } } break; } return runMatrix; }
@Override public void applyInPlace(FastBitmap fastBitmap) { if (fastBitmap.isRGB()) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); LinkedList<IntPoint> examList = new LinkedList(); Color old = new Color(fastBitmap.getRGB(startPoint)); switch (algorithm) { case FourWay: if (!Color.isEqual(old, replace)) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeLast(); old = new Color(fastBitmap.getRGB(p)); if (!Color.isEqual(old, replace)) { int x = p.x; int y = p.y; fastBitmap.setRGB(x, y, replace); if (y - 1 > 0) { examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor } if (y + 1 < width) { examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor } if (x + 1 < height) { examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor } if (x - 1 > 0) { examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor } } } } break; case EightWay: if (!Color.isEqual(old, replace)) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeFirst(); if (Color.isEqual(old, replace)) { int x = p.x; int y = p.y; fastBitmap.setRGB(x, y, replace); if ((x - 1 > 0) && (y - 1 > 0)) { examList.addFirst(new IntPoint(x - 1, y - 1)); // check north-west neighbor } if (x - 1 > 0) { examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor } if ((x + 1 < height) && (y + 1 < width)) { examList.addFirst(new IntPoint(x + 1, y + 1)); // check north-east neighbor } if (y - 1 > 0) { examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor } if (y + 1 < width) { examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor } if ((x + 1 < height) && (y - 1 > 0)) { examList.addFirst(new IntPoint(x + 1, y - 1)); // check south-west neighbor } if (x + 1 < height) { examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor } if ((x + 1 < height) && (y + 1 < width)) { examList.addFirst(new IntPoint(x + 1, y + 1)); // check south-east neighbor } } } } break; } } else if (fastBitmap.isGrayscale()) { int width = fastBitmap.getWidth(); int height = fastBitmap.getHeight(); LinkedList<IntPoint> examList = new LinkedList(); int iGray = fastBitmap.getGray(startPoint); int _gray = gray; int _Gray = _gray; switch (algorithm) { case FourWay: if (iGray != _Gray) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeLast(); _gray = fastBitmap.getGray(p.x, p.y); _Gray = _gray; if (_Gray == iGray) { int x = p.x; int y = p.y; fastBitmap.setGray(x, y, gray); if (y - 1 > 0) { examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor } if (y + 1 < width) { examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor } if (x + 1 < height) { examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor } if (x - 1 > 0) { examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor } } } } break; case EightWay: if (iGray != _Gray) { examList.addFirst(new IntPoint(startPoint)); while (examList.size() > 0) { IntPoint p = examList.removeFirst(); _gray = fastBitmap.getGray(p.x, p.y); _Gray = _gray; if (_Gray == iGray) { int x = p.x; int y = p.y; fastBitmap.setGray(x, y, gray); if ((x - 1 > 0) && (y - 1 > 0)) { examList.addFirst(new IntPoint(x - 1, y - 1)); // check north-west neighbor } if (x - 1 > 0) { examList.addFirst(new IntPoint(x - 1, y)); // check north neighbor } if ((x + 1 < height) && (y + 1 < width)) { examList.addFirst(new IntPoint(x + 1, y + 1)); // check north-east neighbor } if (y - 1 > 0) { examList.addFirst(new IntPoint(x, y - 1)); // check west neighbor } if (y + 1 < width) { examList.addFirst(new IntPoint(x, y + 1)); // check east neighbor } if ((x + 1 < height) && (y - 1 > 0)) { examList.addFirst(new IntPoint(x + 1, y - 1)); // check south-west neighbor } if (x + 1 < height) { examList.addFirst(new IntPoint(x + 1, y)); // check south neighbor } if ((x + 1 < height) && (y + 1 < width)) { examList.addFirst(new IntPoint(x + 1, y + 1)); // check south-east neighbor } } } } break; } } else { throw new IllegalArgumentException("Flood fill only works in RGB and grayscale images."); } }
private void cleanupMaxima(FastBitmap outIp, FastBitmap typeP, long[] maxPoints) { int width = outIp.getWidth(); int height = outIp.getHeight(); byte[] pixels = outIp.getGrayData(); byte[] types = typeP.getGrayData(); int nMax = maxPoints.length; int[] pList = new int[width * height]; for (int iMax = nMax - 1; iMax >= 0; iMax--) { int offset0 = (int) maxPoints[iMax]; // type cast gets lower 32 bits where pixel offset is encoded if ((types[offset0] & ((byte) 8 | (byte) 64)) != 0) continue; int level = pixels[offset0] & 255; int loLevel = level + 1; pList[0] = offset0; // we start the list at the current maximum types[offset0] |= (byte) 2; // mark first point as listed int listLen = 1; // number of elements in the list int lastLen = 1; int listI = 0; // index of current element in the list boolean saddleFound = false; while (!saddleFound && loLevel > 0) { loLevel--; lastLen = listLen; // remember end of list for previous level listI = 0; // in each level, start analyzing the neighbors of all pixels do { // for all pixels listed so far int offset = pList[listI]; int x = offset % width; int y = offset / width; boolean isInner = (y != 0 && y != height - 1) && (x != 0 && x != width - 1); // not necessary, but faster than isWithin for (int d = 0; d < 8; d++) { // analyze all neighbors (in 8 directions) at the same level int offset2 = offset + dirOffset[d]; if ((isInner || isWithin(x, y, d, width, height)) && (types[offset2] & (byte) 2) == 0) { if ((types[offset2] & (byte) 8) != 0 || (((types[offset2] & (byte) 64) != 0) && (pixels[offset2] & 255) >= loLevel)) { saddleFound = true; // we have reached a point touching a "true" maximum... // if (xList[0]==122) IJ.write("saddle found at level="+loLevel+"; // x,y="+xList[listI]+","+yList[listI]+", dir="+d); break; // ...or a level not lower, but touching a "true" maximum } else if ((pixels[offset2] & 255) >= loLevel && (types[offset2] & (byte) 64) == 0) { pList[listLen] = offset2; // xList[listLen] = x+DIR_X_OFFSET[d]; // yList[listLen] = x+DIR_Y_OFFSET[d]; listLen++; // we have found a new point to be processed types[offset2] |= (byte) 2; } } // if isWithin & not (byte)2 } // for directions d if (saddleFound) break; // no reason to search any further listI++; } while (listI < listLen); } // while !levelFound && loLevel>=0 for (listI = 0; listI < listLen; listI++) // reset attribute since we may come to this place again types[pList[listI]] &= ~(byte) 2; for (listI = 0; listI < lastLen; listI++) { // for all points higher than the level of the saddle point int offset = pList[listI]; pixels[offset] = (byte) loLevel; // set pixel value to the level of the saddle point types[offset] |= (byte) 64; // mark as processed: there can't be a local maximum in this area } } // for all maxima iMax }
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; }