package edu.berkeley.cs.nlp.ocular.preprocessing; import java.io.File; import java.util.Arrays; import edu.berkeley.cs.nlp.ocular.image.ImageUtils; import tberg.murphy.arrays.a; import tberg.murphy.fileio.f; import tberg.murphy.tuple.Pair; /** * @author Taylor Berg-Kirkpatrick (tberg@eecs.berkeley.edu) */ public class Cropper { public static final int NUM_CROP_POINTS = 200; public static final double HORIZ_MIN_CENTER_SEG_RATIO = 0.6; public static final double VERT_MIN_CENTER_SEG_RATIO = 0.6; public static final double HORIZ_GROW_RATIO = 0.03; public static final double VERT_GROW_RATIO = 0.0; public static final double INIT_SEG_WEIGHT = 1.0; public static final double CENTER_SEG_WEIGHT = 4.0; public static final double FINAL_SEG_WEIGHT = 1.0; public static final double CONVOLVE_DIST_RATIO = 0.0015; public static double[][] crop(double[][] levels, double binarizeThreshold) { double[][] binaryLevels = a.copy(levels); Binarizer.binarizeGlobal(binarizeThreshold, binaryLevels); levels = a.transpose(levels); binaryLevels = a.transpose(binaryLevels); { double[] varProfile = totalVariationProfile(a.transpose(convolveRows(a.transpose(binaryLevels), (int) (CONVOLVE_DIST_RATIO*binaryLevels.length)))); Pair<Integer,Integer> seg = singleColumnSegment(varProfile, HORIZ_MIN_CENTER_SEG_RATIO); levels = Arrays.copyOfRange(levels, Math.max(0, seg.getFirst()-(int) (levels.length*HORIZ_GROW_RATIO)), Math.min(levels.length, seg.getSecond()+(int) (levels.length*HORIZ_GROW_RATIO))); } levels = a.transpose(levels); binaryLevels = a.transpose(binaryLevels); { double[] varProfile = totalVariationProfile(a.transpose(convolveRows(a.transpose(binaryLevels), (int) (CONVOLVE_DIST_RATIO*levels.length)))); Pair<Integer,Integer> seg = singleColumnSegment(varProfile, VERT_MIN_CENTER_SEG_RATIO); levels = Arrays.copyOfRange(levels, Math.max(0, seg.getFirst()-(int) (levels.length*HORIZ_GROW_RATIO)), Math.min(levels.length, seg.getSecond()+(int) (levels.length*HORIZ_GROW_RATIO))); } return levels; } private static double[] totalVariationProfile(double[][] levels) { double[] varProfile = new double[levels.length]; for (int i=0; i<levels.length; ++i) { for (int j=0; j<levels[i].length-1; ++j) { varProfile[i] += Math.abs(levels[i][j] - levels[i][j+1]) / (levels[i].length-1); } } return varProfile; } public static double[][] convolveRows(double[][] binarizedLevels, int pixels) { double[][] result = new double[binarizedLevels.length][binarizedLevels[0].length]; for (int i=0; i<result.length; ++i) { Arrays.fill(result[i], ImageUtils.MAX_LEVEL); } for (int i=0; i<binarizedLevels.length; ++i) { for (int j=0; j<binarizedLevels[i].length; ++j) { if (binarizedLevels[i][j] < ImageUtils.MAX_LEVEL) { for (int k=Math.max(0,j-pixels); k<=Math.min(binarizedLevels[i].length-1,j+pixels); ++k) { result[i][k] = 0; } } } } return result; } private static Pair<Integer,Integer> singleColumnSegment(double[] varProfile, double minCenterWidthFrac) { int minCenterWidth = (int) (minCenterWidthFrac * varProfile.length); int bestI = -1; int bestJ = -1; double bestObjective = Double.POSITIVE_INFINITY; for (int i=0; i<varProfile.length; i+=varProfile.length/NUM_CROP_POINTS) { for (int j=i+minCenterWidth; j<varProfile.length; j+=varProfile.length/NUM_CROP_POINTS) { double val = evalSingleSegmentation(i, j, varProfile); if (val < bestObjective) { bestObjective = val; bestI = i; bestJ = j; } } } return Pair.makePair(bestI, bestJ); } private static double evalSingleSegmentation(int i, int j, double[] totalVars) { double result = 0.0; { double mean = 0.0; for (int ii=0; ii<i; ++ii) { mean += totalVars[ii] / i; } double var = 0.0; for (int ii=0; ii<i; ++ii) { double diff = (mean - totalVars[ii]); var += diff*diff / i; } result += INIT_SEG_WEIGHT * var; } { double mean = 0.0; for (int ii=i; ii<j; ++ii) { mean += totalVars[ii] / (j-i); } double var = 0.0; for (int ii=i; ii<j; ++ii) { double diff = (mean - totalVars[ii]); var += diff*diff / (j-i); } result += CENTER_SEG_WEIGHT * var; } { double mean = 0.0; for (int ii=j; ii<totalVars.length; ++ii) { mean += totalVars[ii] / (totalVars.length - j); } double var = 0.0; for (int ii=j; ii<totalVars.length; ++ii) { double diff = (mean - totalVars[ii]); var += diff*diff / (totalVars.length - j); } result += FINAL_SEG_WEIGHT * var; } return result; } public static void main(String[] args) { String path = "/Users/tberg/Desktop/test_crop/"; File dir = new File(path); // String[] names = dir.list(); String[] names = new String[] {"axc0032-0.jpg", "axc0037-0.jpg"}; for (String name : names) { double[][] levels = ImageUtils.getLevels(f.readImage(path+"/"+name)); // ImageUtils.display(ImageUtils.makeImage(levels)); double[][] rotLevels = Straightener.straighten(levels); // ImageUtils.display(ImageUtils.makeImage(rotLevels)); double[][] cropLevels = Cropper.crop(rotLevels, 0.12); ImageUtils.display(ImageUtils.makeImage(cropLevels)); } } // public static double[][] crop(double[][] levels, double binarizeThreshold) { // double[][] binaryLevels = a.copy(levels); // Binarizer.binarizeGlobal(binarizeThreshold, binaryLevels); // // levels = a.transpose(levels); // binaryLevels = a.transpose(binaryLevels); // if (Main.twoColumn) { // double[] varProfile = totalVariationProfile(a.transpose(convolveRows(a.transpose(binaryLevels), (int) (CONVOLVE_DIST_RATION*binaryLevels.length)))); // int[] seg = doubleColumnSegment(varProfile, HORIZ_MIN_CENTER_SEG_RATIO); // System.out.println(a.toString(seg)); // double[][] levelsCol1 = Arrays.copyOfRange(levels, Math.max(0, seg[0]-(int) (levels.length*HORIZ_GROW_RATIO)), Math.min(levels.length, seg[1]+(int) (levels.length*HORIZ_GROW_RATIO))); // double[][] levelsCol2 = Arrays.copyOfRange(levels, Math.max(0, seg[2]-(int) (levels.length*HORIZ_GROW_RATIO)), Math.min(levels.length, seg[3]+(int) (levels.length*HORIZ_GROW_RATIO))); // levels = a.transpose(a.append(a.transpose(levelsCol1), a.transpose(levelsCol2))); // } else { // double[] varProfile = totalVariationProfile(a.transpose(convolveRows(a.transpose(binaryLevels), (int) (CONVOLVE_DIST_RATION*binaryLevels.length)))); // Pair<Integer,Integer> seg = singleColumnSegment(varProfile, HORIZ_MIN_CENTER_SEG_RATIO); // levels = Arrays.copyOfRange(levels, Math.max(0, seg.getFirst()-(int) (levels.length*HORIZ_GROW_RATIO)), Math.min(levels.length, seg.getSecond()+(int) (levels.length*HORIZ_GROW_RATIO))); // } // levels = a.transpose(levels); // binaryLevels = a.transpose(binaryLevels); // { // double[] varProfile = totalVariationProfile(a.transpose(convolveRows(a.transpose(binaryLevels), (int) (CONVOLVE_DIST_RATION*levels.length)))); // Pair<Integer,Integer> seg = singleColumnSegment(varProfile, VERT_MIN_CENTER_SEG_RATIO); // levels = Arrays.copyOfRange(levels, Math.max(0, seg.getFirst()-(int) (levels.length*HORIZ_GROW_RATIO)), Math.min(levels.length, seg.getSecond()+(int) (levels.length*HORIZ_GROW_RATIO))); // } // return levels; //} // private static int[] doubleColumnSegment(double[] varProfile, double minCenterWidthFrac) { // int minCenterWidth = (int) (minCenterWidthFrac * varProfile.length); // int bestI = -1; // int bestJ = -1; // int bestI2 = -1; // int bestJ2 = -1; // double bestObjective = Double.POSITIVE_INFINITY; // for (int i = 0; i < varProfile.length; i += varProfile.length / NUM_CROP_POINTS) { // for (int j2 = i + minCenterWidth; j2 < varProfile.length; j2 += varProfile.length / NUM_CROP_POINTS) { // for (int j = i + 1; j < j2 - 1; j += varProfile.length / NUM_CROP_POINTS) { // for (int i2 = j + 1; i2 < j2; i2 += varProfile.length / NUM_CROP_POINTS) { // double val = evalDoubleSegmentation(i, j, i2, j2, varProfile); // if (val < bestObjective) { // bestObjective = val; // bestI = i; // bestJ = j; // bestI2 = i2; // bestJ2 = j2; // } // } // } // } // } // return new int[] {bestI, bestJ, bestI2, bestJ2}; // } // // private static double evalDoubleSegmentation(int i, int j, int i2, int j2, double[] totalVars) { // double result = 0.0; // { // double mean = 0.0; // for (int ii=0; ii<i; ++ii) { // mean += totalVars[ii] / i; // } // double var = 0.0; // for (int ii=0; ii<i; ++ii) { // double diff = (mean - totalVars[ii]); // var += diff*diff / i; // } // result += INIT_SEG_WEIGHT * var; // } // { // double mean = 0.0; // for (int ii=i; ii<j; ++ii) { // mean += totalVars[ii] / (j-i); // } // double var = 0.0; // for (int ii=i; ii<j; ++ii) { // double diff = (mean - totalVars[ii]); // var += diff*diff / (j-i); // } // result += CENTER_SEG_WEIGHT * var; // } // { // double mean = 0.0; // for (int ii=j; ii<i2; ++ii) { // mean += totalVars[ii] / (j-i); // } // double var = 0.0; // for (int ii=j; ii<i2; ++ii) { // double diff = (mean - totalVars[ii]); // var += diff*diff / (j-i); // } // result += CENTER_SEG_WEIGHT * var; // } // { // double mean = 0.0; // for (int ii=i2; ii<j2; ++ii) { // mean += totalVars[ii] / (j-i); // } // double var = 0.0; // for (int ii=i2; ii<j2; ++ii) { // double diff = (mean - totalVars[ii]); // var += diff*diff / (j-i); // } // result += CENTER_SEG_WEIGHT * var; // } // { // double mean = 0.0; // for (int ii=j; ii<totalVars.length; ++ii) { // mean += totalVars[ii] / (totalVars.length - j); // } // double var = 0.0; // for (int ii=j; ii<totalVars.length; ++ii) { // double diff = (mean - totalVars[ii]); // var += diff*diff / (totalVars.length - j); // } // result += FINAL_SEG_WEIGHT * var; // } // return result; // } }