package jp.crwdev.app.imagefilter; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; import jp.crwdev.app.constant.Constant; import jp.crwdev.app.interfaces.IImageFilter; public class AutoCropFilter implements IImageFilter { private static final int mCheckOffset = 3; private static final int mDefaultCropMargin = 30; private static final int mLooseMargin = 5; private static final int mNoiseLimit = 8; // ノイズ許容pixel数 private static final int mRelapseLimit = 6; // ノイズじゃないと判断したときに戻る最大pixel数 private int mWhiteColorThreshold = 0x40; @Override public BufferedImage filter(BufferedImage image, ImageFilterParam param) { if(param == null){ return image; } if(param.isColorPageCrop() || param.isPictPageCrop() || param.isTextPageCrop() || param.isFullPageCrop()){ return image; } if((param.getPageType() == Constant.PAGETYPE_COLOR && !param.isColorPageAutoCrop()) || (param.getPageType() == Constant.PAGETYPE_PICT && !param.isPictPageAutoCrop()) || (param.getPageType() == Constant.PAGETYPE_TEXT && !param.isTextPageAutoCrop()) || (param.getPageType() == Constant.PAGETYPE_AUTO && !param.isFullPageAutoCrop())){ return image; } int left = 0; int top = 0; int right = 0; int bottom = 0; switch(param.getPageType()){ case Constant.PAGETYPE_COLOR: left = param.getColorPageCropLeft(); top = param.getColorPageCropTop(); right = param.getColorPageCropRight(); bottom = param.getColorPageCropBottom(); mWhiteColorThreshold = param.getColorPageAutoCropThreshold(); break; case Constant.PAGETYPE_PICT: left = param.getPictPageCropLeft(); top = param.getPictPageCropTop(); right = param.getPictPageCropRight(); bottom = param.getPictPageCropBottom(); mWhiteColorThreshold = param.getPictPageAutoCropThreshold(); break; case Constant.PAGETYPE_TEXT: left = param.getTextPageCropLeft(); top = param.getTextPageCropTop(); right = param.getTextPageCropRight(); bottom = param.getTextPageCropBottom(); mWhiteColorThreshold = param.getTextPageAutoCropThreshold(); break; case Constant.PAGETYPE_AUTO: default: left = param.getFullPageCropLeft(); top = param.getFullPageCropTop(); right = param.getFullPageCropRight(); bottom = param.getFullPageCropBottom(); mWhiteColorThreshold = param.getFullPageAutoCropThreshold(); break; } if(mWhiteColorThreshold == 0){ mWhiteColorThreshold = 0x40; } if(left == 0 && right == 0 && top == 0 && bottom == 0){ left = mDefaultCropMargin; top = mDefaultCropMargin; right = mDefaultCropMargin; bottom = mDefaultCropMargin; } //Rectangle subRect = new Rectangle(); //Rectangle rect = getAutoCropRect2(image, left, top, right, bottom, subRect); Rectangle rect = getAutoCropRect2(image, left, top, right, bottom); if(!rect.isEmpty()){ if(param.isPreview()){ Graphics2D g = image.createGraphics(); g.setColor(Color.BLUE); g.drawRect(rect.x, rect.y, rect.width, rect.height); //g.setColor(Color.RED); //g.drawRect(subRect.x, subRect.y, subRect.width, subRect.height); g.dispose(); } else{ return image.getSubimage(rect.x, rect.y, rect.width, rect.height); } } return image; } @SuppressWarnings("unused") private Rectangle getAutoCropRect(BufferedImage image, int leftMargin, int topMargin, int rightMargin, int bottomMargin){ Rectangle rect = new Rectangle(); // EdgeDetectionFilter edge = new EdgeDetectionFilter(); // image = edge.filter(image, null); int width = image.getWidth(); int height = image.getHeight(); int left = leftMargin == 0 ? width-1 : leftMargin; int right = rightMargin == 0 ? 0 : width - rightMargin - 1; int top = topMargin == 0 ? height-1 : topMargin; int bottom = bottomMargin == 0 ? 0 : height - bottomMargin - 1; boolean found = false; for(int y=0; y<height; y+= mCheckOffset){ for(int x=0; x<leftMargin && x<left; x+= 1){ if(!isWhiteColor(image.getRGB(x, y))){ if(x < left){ left = x; found = true; } break; } } } if(found){ if(mLooseMargin > 0){ left -= Math.min(10,(left / mLooseMargin)) + 1; }else{ left -= 1; } } left = Math.max(0, left); found = false; for(int y=0; y<height; y+= mCheckOffset){ for(int x=width-1; x>=width-rightMargin-1 && x>=right; x-= 1){ if(!isWhiteColor(image.getRGB(x, y))){ if(x > right){ right = x; found = true; } break; } } } if(found){ if(mLooseMargin > 0){ int offset = width - right; right += Math.min(10,(offset / mLooseMargin)) + 1; }else{ right += 1; } } right = Math.min(width-1, right); found = false; for(int x=0; x<width; x+= mCheckOffset){ for(int y=0; y<topMargin && y<top; y+= 1){ if(!isWhiteColor(image.getRGB(x, y))){ if(y < top){ top = y; found = true; } break; } } } if(found){ if(mLooseMargin > 0){ top -= Math.min(10,(top / mLooseMargin)) + 1; }else{ top -= 1; } } top = Math.max(0, top); found = false; for(int x=0; x<width; x+= mCheckOffset){ for(int y=height-1; y>=height-bottomMargin-1 && y>=bottom; y-= 1){ if(!isWhiteColor(image.getRGB(x, y))){ if(y > bottom){ bottom = y; found = true; } break; } } } if(found){ if(mLooseMargin > 0){ int offset = height - bottom; bottom += Math.min(10,(offset / mLooseMargin)) + 1; }else{ bottom += 1; } } bottom = Math.min(height-1, bottom); if(left >= right){ left = 0; right = width-1; } if(top >= bottom){ top = 0; bottom = height-1; } rect.setBounds(left, top, right-left, bottom-top); return rect; } private Rectangle getAutoCropRect2(BufferedImage image, int leftMargin, int topMargin, int rightMargin, int bottomMargin){ Rectangle rect = new Rectangle(); int top = deleteCountTop(image, topMargin); int bottom = deleteCountBottom(image, bottomMargin); if(top < 0 && bottom < 0){ // not found top = topMargin; bottom = bottomMargin; } else if(top >= 0 && bottom >= 0){ // 両方引っかかった top = topMargin - top; bottom = bottomMargin - bottom; } else if(top < 0){ // 上は見つからなかったが下は見つかった top = deleteCountTop(image, topMargin + bottom); // 上を再チェック if(top < 0){ top = topMargin + bottom; // 見つからなかったので全削り }else{ top = topMargin + bottom - top; } bottom = bottomMargin - bottom; } else{ // 下は見つからなかったが上は見つかった bottom = deleteCountBottom(image, bottomMargin + top); // 上を再チェック if(bottom < 0){ bottom = bottomMargin + top; // 見つからなかったので全削り }else{ bottom = bottomMargin + top - bottom; } top = topMargin - top; } int left = deleteCountLeft(image, leftMargin); int right = deleteCountRight(image, rightMargin); if(left < 0 && right < 0){ // not found left = leftMargin; right = rightMargin; } else if(left >= 0 && right >= 0){ // 両方引っかかった left = leftMargin - left; right = rightMargin - right; } else if(left < 0){ // 上は見つからなかったが下は見つかった left = deleteCountLeft(image, leftMargin + right); // 上を再チェック if(left < 0){ left = leftMargin + right; // 見つからなかったので全削り }else{ left = leftMargin + right - left; } right = rightMargin - right; } else{ // 下は見つからなかったが上は見つかった right = deleteCountRight(image, rightMargin + left); // 上を再チェック if(right < 0){ right = rightMargin + left; // 見つからなかったので全削り }else{ right = rightMargin + left - right; } left = leftMargin - left; } int width = image.getWidth(); int height = image.getHeight(); left = Math.max(0, left-5); // ギリギリでクリップするとReaderで縮小した時に影が出来てしまうので余裕を持たせる right = Math.max(0, right-5); top = Math.max(0, top-1); bottom = Math.max(0, bottom-1); rect.setBounds(left, top, width - left - right, height - top - bottom); return rect; } private int deleteCountLeft(BufferedImage image, int max){ @SuppressWarnings("unused") int width = image.getWidth(); int height = image.getHeight(); int first = -1; int left = 0; int count = 0; boolean found = false; for(int x=0; x<max && !found; x++){ for(int y=0; y<height; y++){ if(!isWhiteColor(image.getRGB(x, y))){ if(first < 0){ first = x; } if(x > left){ left = x; } if(++count >= mNoiseLimit){ found = true; break; } } } } if(found){ if(left - first <= mRelapseLimit){ left = first; } return (max - left); } else{ return -1; } } private int deleteCountTop(BufferedImage image, int max){ int width = image.getWidth(); @SuppressWarnings("unused") int height = image.getHeight(); int first = -1; int top = 0; int count = 0; boolean found = false; for(int y=0; y<max && !found; y++){ for(int x=0; x<width; x++){ if(!isWhiteColor(image.getRGB(x, y))){ if(first < 0){ first = y; } if(y > top){ top = y; } if(++count >= mNoiseLimit){ found = true; break; } } } } if(found){ if(top - first <= mRelapseLimit){ top = first; } return (max - top); } else{ return -1; } } private int deleteCountRight(BufferedImage image, int max){ int width = image.getWidth(); int height = image.getHeight(); int first = -1; int right = 0; int count = 0; boolean found = false; for(int x=0; x<max && !found; x++){ for(int y=0; y<height; y++){ if(!isWhiteColor(image.getRGB(width - x - 1, y))){ if(first < 0){ first = x; } if(x > right){ right = x; } if(++count >= mNoiseLimit){ found = true; break; } } } } if(found){ if(right - first <= mRelapseLimit){ right = first; } return (max - right); } else{ return -1; } } private int deleteCountBottom(BufferedImage image, int max){ int width = image.getWidth(); int height = image.getHeight(); int first = -1; int bottom = 0; int count = 0; boolean found = false; for(int y=0; y<max && !found; y++){ for(int x=0; x<width; x++){ if(!isWhiteColor(image.getRGB(x, height - y - 1))){ if(first < 0){ first = y; } if(y > bottom){ bottom = y; } if(++count >= mNoiseLimit){ found = true; break; } } } } if(found){ if(bottom - first <= mRelapseLimit){ bottom = first; } return (max - bottom); } else{ return -1; } } public boolean isWhiteColor(int color){ int r = (color >> 16) & 0xff; int g = (color >> 8) & 0xff; int b = color & 0xff; if(r >= mWhiteColorThreshold && g >= mWhiteColorThreshold && b >= mWhiteColorThreshold){ return true; } return false; } public boolean isBlackColor(int color){ int r = (color >> 16) & 0xff; int g = (color >> 8) & 0xff; int b = color & 0xff; if(r <= 0x50 && g <= 0x50 && b <= 0x50){ return true; } return false; } }