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.threshold.adaptive; 011 012import ij.process.ByteProcessor; 013import imagingbook.pub.threshold.BackgroundMode; 014import imagingbook.pub.threshold.global.OtsuThresholder; 015 016/** 017 * This thresholder splits the image into non-overlapping square 018 * sub-images, computes the optimal threshold within each sub-image 019 * (using an Otsu thresholder) and interpolates linearly between 020 * these local thresholds. 021 */ 022public class InterpolatingThresholder extends AdaptiveThresholder { 023 024 public static class Parameters { 025 public int tileSize = 32; 026 public BackgroundMode bgMode = BackgroundMode.DARK; 027 } 028 029 private final Parameters params; 030 031 public InterpolatingThresholder() { 032 this.params = new Parameters(); 033 } 034 035 public InterpolatingThresholder(Parameters params) { 036 this.params = params; 037 } 038 039 @Override 040 public ByteProcessor getThreshold(ByteProcessor ip) { 041 final int W = ip.getWidth(); 042 final int H = ip.getHeight(); 043 final int tileSize = params.tileSize; 044 045 // determine number of tiles 046 int nW = (W % tileSize == 0) ? (W / tileSize + 1) : (W / tileSize + 2); 047 int nH = (H % tileSize == 0) ? (H / tileSize + 1) : (H / tileSize + 2); 048 049 int[][] tiles = new int[nW][nH]; 050 int s0 = tileSize / 2; // center of title (s0 + s1 = tileSize) 051// int s1 = tileSize - s0; 052 053 // compute threshold for each tile 054 int[] h = new int[256]; 055 OtsuThresholder thr = new OtsuThresholder(); 056 057 int q_ = (params.bgMode == BackgroundMode.DARK) ? 256 : 0; 058 059 for (int j = 0, v0 = 0; j < nH; j++, v0 += tileSize) { 060 for (int i = 0, u0 = 0; i < nW; i++, u0 += tileSize) { 061 getSubimageHistogram(ip, u0 - s0, v0 - s0, tileSize, h); 062 int q = thr.getThreshold(h); 063 if (q < 0) q = q_; // no threshold found in this tile 064 tiles[i][j] = q; 065 //IJ.log(i + "/" + j + ": " + q); 066 } 067 } 068 069 ByteProcessor thrIp = new ByteProcessor(W, H); 070 071 for (int j = 0, v0 = 0; j < nH; j++, v0 += tileSize) { 072 for (int i = 0, u0 = 0; i < nW; i++, u0 += tileSize) { 073 // Rectangle re = new Rectangle(u0-s0, v0-s0, u0-s0+tileSize, v0-s0+tileSize); 074 for (int v = v0 - s0; v < v0 - s0 + tileSize; v++) { 075 for (int u = u0 - s0; u < u0 - s0 + tileSize; u++) { 076 thrIp.putPixel(u, v, tiles[i][j]); 077 } 078 } 079 } 080 } 081 082 // linearly interpolate 083 for (int j = 0, v0 = 0; j < nH - 1; j++, v0 += tileSize) { 084 for (int i = 0, u0 = 0; i < nW - 1; i++, u0 += tileSize) { 085 int A = tiles[i][j]; 086 int B = tiles[i + 1][j]; 087 int C = tiles[i][j + 1]; 088 int D = tiles[i + 1][j + 1]; 089 090 // interpolate within [u0, v0, u0 + tileSize, v0 + tileSize] 091 for (int v = v0; v < v0 + tileSize; v++) { 092 double dy = (double) (v - v0) / tileSize; 093 double AC = A + dy * (C - A); 094 double BD = B + dy * (D - B); 095 for (int u = u0; u < u0 + tileSize; u++) { 096 double dx = (double) (u - u0) / tileSize; 097 double ABCD = AC + dx * (BD - AC); 098 // thrIp.putPixel(u,v,tiles[i][j]); 099 thrIp.putPixel(u, v, (int) Math.rint(ABCD)); 100 } 101 } 102 103 } 104 } 105 106 return thrIp; 107 } 108 109 private void getSubimageHistogram(ByteProcessor ip, int u0, int v0, int size, int[] h) { 110 for (int i = 0; i < h.length; i++) { 111 h[i] = 0; 112 } 113 for (int v = v0; v < v0 + size; v++) { 114 for (int u = u0; u < u0 + size; u++) { 115 int p = getPaddedPixel(ip, u, v); 116 h[p]++; 117 } 118 } 119 } 120 121}