Gast commit!
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.File;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class AwtPlatformImage implements PlatformImage<Image> {
|
||||
|
||||
BufferedImage image;
|
||||
WritableRaster raster;
|
||||
|
||||
public AwtPlatformImage() {
|
||||
|
||||
}
|
||||
public AwtPlatformImage(PlatformImage image) {
|
||||
create(image.getWidth(), image.getHeight());
|
||||
|
||||
}
|
||||
|
||||
public AwtPlatformImage(String path) throws Exception {
|
||||
load(path);
|
||||
}
|
||||
|
||||
public AwtPlatformImage(int w, int h) throws Exception {
|
||||
create(w, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Image img) {
|
||||
image = (BufferedImage) img;
|
||||
raster = (WritableRaster) image.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(String path) throws Exception {
|
||||
image = ImageIO.read(new File(path));
|
||||
raster = (WritableRaster) image.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(int width, int height) {
|
||||
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
raster = image.getData().createCompatibleWritableRaster();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return raster.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return raster.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPixel(int x, int y, int[] color) {
|
||||
try {
|
||||
raster.setPixel(x, y, color);
|
||||
}catch (Exception e){
|
||||
ImageUtils.log("setPixel", raster.getHeight(), raster.getHeight(), x,y);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage() {
|
||||
image.setData(raster);
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getPixel(int x, int y) {
|
||||
int[] pixel = new int[4];
|
||||
raster.getPixel(x, y, pixel);
|
||||
return pixel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRedPixel(int x, int y) {
|
||||
int[] pixel = new int[4];
|
||||
raster.getPixel(x, y, pixel);
|
||||
return pixel[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlackPixel(int x, int y) {
|
||||
return getRedPixel(x, y) < 180;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import mobi.librera.smartreflow.model.Line;
|
||||
import mobi.librera.smartreflow.model.Rect;
|
||||
import mobi.librera.smartreflow.model.Word;
|
||||
|
||||
public class ImageUtils {
|
||||
|
||||
|
||||
public static Logger platformLogger = new PrintLnLogger();
|
||||
|
||||
public static void setBackgroundColor(PlatformImage image, int[] color) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
image.setPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeWhiteBegin(PlatformImage img, Rect line) {
|
||||
for (int x = line.x1; x < line.x2; x++) {
|
||||
for (int y = line.y1; y < line.y2; y++) {
|
||||
if (ImageUtils.isBlackPixel(img, x, y)) {
|
||||
line.x1 = x;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeWhiteEnd(PlatformImage img, Rect line) {
|
||||
for (int x = line.x2; x > line.x1; x--) {
|
||||
for (int y = line.y1; y < line.y2; y++) {
|
||||
if (ImageUtils.isBlackPixel(img, x, y)) {
|
||||
line.x2 = x;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isBlackPixel(PlatformImage image, int x, int y) {
|
||||
return image.getPixel(x, y)[0] < 180;
|
||||
|
||||
}
|
||||
|
||||
public static boolean isWhiteHorizontal(PlatformImage img, int y, int x1, int x2) {
|
||||
for (int x = x1; x < x2; x += 1) {
|
||||
if (ImageUtils.isBlackPixel(img, x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isWhiteVertical(PlatformImage img, int x, int y1, int y2) {
|
||||
for (int y = y1; y < y2; y += 1) {
|
||||
if (ImageUtils.isBlackPixel(img, x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isBlankVertical(PlatformImage img, int x, int y1, int y2, int dx) {
|
||||
boolean isBlank = true;
|
||||
for (int y = y1; y < y2; y += dx) {
|
||||
|
||||
if (img.isBlackPixel(x, y)) {
|
||||
isBlank = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isBlank;
|
||||
}
|
||||
|
||||
public static void drawRect(PlatformImage image, Rect r, int[] color) {
|
||||
for (int i = r.x1; i < r.x2; i++) {
|
||||
image.setPixel(i, r.y1, color);
|
||||
image.setPixel(i, r.y2, color);
|
||||
}
|
||||
for (int i = r.y1; i < r.y2; i++) {
|
||||
image.setPixel(r.x1, i, color);
|
||||
image.setPixel(r.x2, i, color);
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawWord(Rect w, int dx, int dy, PlatformImage input, PlatformImage output) {
|
||||
for (int i = w.x1; i < w.x2; i++) {
|
||||
for (int j = w.y1; j < w.y2; j++) {
|
||||
int pixel[] = input.getPixel(i, j);
|
||||
final int x = dx + (i - w.x1);
|
||||
final int y = dy + (j - w.y1);
|
||||
if(x<output.getWidth() && y< output.getHeight()) {
|
||||
output.setPixel(x, y, pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void drawRect(PlatformImage image, int x, int y, int x1, int y1, int[] color) {
|
||||
for (int i = x; i < x1; i++) {
|
||||
image.setPixel(i, y, color);
|
||||
image.setPixel(i, y1, color);
|
||||
}
|
||||
for (int i = y; i < y1; i++) {
|
||||
image.setPixel(x, i, color);
|
||||
image.setPixel(x1, i, color);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyRect(PlatformImage input, PlatformImage output, int x, int y, int x1, int y1) {
|
||||
for (int i = x; i < x1; i++) {
|
||||
for (int j = y; j < y1; j++) {
|
||||
final int[] pixel = input.getPixel(i, j);
|
||||
output.setPixel(i, j, pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(Object... strings) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (Object str : strings) {
|
||||
s.append(str + "|");
|
||||
}
|
||||
platformLogger.log(s.toString());
|
||||
}
|
||||
|
||||
public static List<Rect> splitHorizontal(PlatformImage img, Rect rect, int minLineHeight) {
|
||||
Rect line = null;
|
||||
List<Rect> rects = new ArrayList<>();
|
||||
boolean prevWhite = true;
|
||||
for (int y = rect.y1; y < rect.y2; y += 1) {
|
||||
boolean isWhite = ImageUtils.isWhiteHorizontal(img, y, rect.x1, rect.x2);
|
||||
|
||||
if (prevWhite && !isWhite) {
|
||||
line = new Line(rect.x1, y);
|
||||
}
|
||||
if (isWhite && !prevWhite) {
|
||||
if (y - line.y1 > minLineHeight) {
|
||||
line.x2 = rect.x2;
|
||||
line.y2 = y;
|
||||
rects.add(line);
|
||||
line = null;
|
||||
}
|
||||
}
|
||||
prevWhite = isWhite;
|
||||
}
|
||||
if (line != null) {
|
||||
line.x2 = rect.x2;
|
||||
line.y2 = rect.y2;
|
||||
rects.add(line);
|
||||
|
||||
}
|
||||
return rects;
|
||||
}
|
||||
|
||||
public static List<Word> splitVertical(PlatformImage img, Rect rect, int minSpaceSize) {
|
||||
Word line = null;
|
||||
List<Word> rects = new ArrayList<>();
|
||||
|
||||
boolean prevWhite = true;
|
||||
int countWhite = 0;
|
||||
for (int x = rect.x1; x < rect.x2; x += 1) {
|
||||
boolean isWhite = ImageUtils.isWhiteVertical(img, x, rect.y1, rect.y2);
|
||||
if (isWhite) {
|
||||
countWhite++;
|
||||
if (countWhite < minSpaceSize) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
countWhite = 0;
|
||||
|
||||
|
||||
if (prevWhite && !isWhite) {
|
||||
line = new Word(x, rect.y1);
|
||||
|
||||
}
|
||||
if (isWhite && !prevWhite) {
|
||||
line.y2 = rect.y2;
|
||||
line.x2 = x;
|
||||
rects.add(line);
|
||||
line = null;
|
||||
|
||||
}
|
||||
prevWhite = isWhite;
|
||||
}
|
||||
if (line != null) {
|
||||
line.y2 = rect.y2;
|
||||
line.x2 = rect.x2;
|
||||
rects.add(line);
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
|
||||
public abstract static class Logger {
|
||||
public abstract void log(String str);
|
||||
}
|
||||
|
||||
public static class PrintLnLogger extends Logger {
|
||||
|
||||
@Override
|
||||
public void log(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
public interface PlatformImage<T> {
|
||||
int[] RED = new int[]{255, 0, 0};
|
||||
int[] GREEN = new int[]{0, 255, 0};
|
||||
int[] BLUE = new int[]{0, 0, 255};
|
||||
int[] WHITE = new int[]{255, 255, 255};
|
||||
int[] BLACK = new int[]{0, 0, 0};
|
||||
int[] YELLOW = new int[]{128, 255, 0};
|
||||
int[] MAROON = new int[]{128, 0, 0};
|
||||
|
||||
|
||||
void create(int width, int height);
|
||||
|
||||
void load(T img);
|
||||
|
||||
int getWidth();
|
||||
|
||||
int getHeight();
|
||||
|
||||
void setPixel(int x, int y, int color[]);
|
||||
|
||||
void load(String path) throws Exception;
|
||||
|
||||
T getImage();
|
||||
|
||||
int[] getPixel(int x, int y);
|
||||
|
||||
int getRedPixel(int x, int y);
|
||||
|
||||
boolean isBlackPixel(int x, int y);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
|
||||
public class SmartImageRunUI {
|
||||
|
||||
static GraphicsConfiguration gc;
|
||||
|
||||
//static String img = "/home/ivan-dev/Downloads/testSM/2.jpg";
|
||||
static String img = "/home/ivan-dev/IdeaProjects/SmartReflow/images/sample1.png";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
runIU(img);
|
||||
}
|
||||
|
||||
private static JFrame runIU(String path) throws Exception {
|
||||
|
||||
AwtPlatformImage image = new AwtPlatformImage(path);
|
||||
|
||||
SmartReflow1 reflow = new SmartReflow1();
|
||||
reflow.process(image);
|
||||
// reflow.isDrawResult = true;
|
||||
// reflow.isDrawResultUsingWords = true;
|
||||
//
|
||||
// reflow.isDrawColums = true;
|
||||
// reflow.isDrawLines = false;
|
||||
// reflow.isDrawWords = true;
|
||||
// reflow.isDrawChars = false;
|
||||
// reflow.isDrawWordsOffsetLeft = true;
|
||||
|
||||
|
||||
AwtPlatformImage out = new AwtPlatformImage();
|
||||
reflow.drawObjects(out);
|
||||
|
||||
final JFrame frame = new JFrame(gc);
|
||||
|
||||
JPanel horizontal = new JPanel();
|
||||
horizontal.setLayout(new BoxLayout(horizontal, BoxLayout.LINE_AXIS));
|
||||
|
||||
|
||||
JPanel vertical = new JPanel();
|
||||
vertical.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
vertical.setLayout(new BoxLayout(vertical, BoxLayout.PAGE_AXIS));
|
||||
vertical.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
|
||||
|
||||
//vertical.add(makeLabel("Original Image"));
|
||||
//Image originalImage = image.getImage();
|
||||
//if (originalImage != null) {
|
||||
//vertical.add(new JLabel(new ImageIcon(originalImage)));
|
||||
//}
|
||||
|
||||
|
||||
Image objects = out.getImage();
|
||||
vertical.add(makeLabel("Objects " + out.getWidth() + "x" + out.getHeight()));
|
||||
|
||||
if (objects != null) {
|
||||
try {
|
||||
vertical.add(new JLabel(new ImageIcon(objects)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
vertical.add(makeLabel("Statistics"));
|
||||
|
||||
for (String line : reflow.getStatistics()) {
|
||||
vertical.add(makeText(line));
|
||||
}
|
||||
|
||||
JPanel vertical2 = new JPanel();
|
||||
vertical2.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
vertical2.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
vertical2.setLayout(new BoxLayout(vertical2, BoxLayout.PAGE_AXIS));
|
||||
|
||||
|
||||
try {
|
||||
final int w = 407;
|
||||
AwtPlatformImage out2 = new AwtPlatformImage(w, w * 2);
|
||||
reflow.reflow(out2);
|
||||
vertical2.add(makeLabel("Reflow " + out2.getWidth() + "x" + out2.getHeight()));
|
||||
|
||||
|
||||
Image reflowed = out2.getImage();
|
||||
if (reflowed != null) {
|
||||
vertical2.add(new JLabel(new ImageIcon(reflowed)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
vertical2.add(makeLabel("Error : " + e.getMessage()));
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
|
||||
|
||||
horizontal.add(vertical);
|
||||
horizontal.add(vertical2);
|
||||
|
||||
|
||||
JPanel main = new JPanel();
|
||||
main.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
main.setLayout(new BoxLayout(main, BoxLayout.PAGE_AXIS));
|
||||
|
||||
final JButton open1 = new JButton("Open");
|
||||
open1.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
open1.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
File my = new File("/home/ivan-dev/IdeaProjects/SmartReflow/src1");
|
||||
if (!my.exists()) {
|
||||
my = new File(System.getProperty("user.dir"));
|
||||
|
||||
}
|
||||
|
||||
JFileChooser fileChooser = null;
|
||||
if (my.exists()) {
|
||||
fileChooser = new JFileChooser(my);
|
||||
} else {
|
||||
fileChooser = new JFileChooser();
|
||||
}
|
||||
|
||||
fileChooser.setFileFilter(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File f) {
|
||||
return f.isDirectory() || f.getPath().endsWith(".png") || f.getPath().endsWith(".jpg");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
int res = fileChooser.showOpenDialog(open1);
|
||||
if (res == JFileChooser.APPROVE_OPTION) {
|
||||
File file = fileChooser.getSelectedFile();
|
||||
//open.setText(file.getPath());
|
||||
//frame.dispose();
|
||||
//frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
|
||||
frame.setVisible(false);
|
||||
|
||||
try {
|
||||
runIU(file.getPath());
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
main.add(open1);
|
||||
main.add(new JScrollPane(horizontal));
|
||||
|
||||
|
||||
frame.add(main);
|
||||
|
||||
frame.setTitle("Librera Native Reflow");
|
||||
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
|
||||
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
frame.setSize((int) screenSize.getWidth(), (int) screenSize.getHeight());
|
||||
frame.setVisible(true);
|
||||
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setResizable(true);
|
||||
|
||||
frame.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent ke) { // handler
|
||||
if (ke.getKeyCode() == ke.VK_ESCAPE) {
|
||||
frame.dispose();
|
||||
} else {
|
||||
}
|
||||
}
|
||||
});
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static JLabel makeLabel(String txt) {
|
||||
JLabel originalImage = new JLabel(txt);
|
||||
originalImage.setFont(new Font("Serif", Font.BOLD, 28));
|
||||
return originalImage;
|
||||
}
|
||||
|
||||
private static JLabel makeText(String txt) {
|
||||
JLabel originalImage = new JLabel(txt);
|
||||
originalImage.setFont(new Font("Monospaced", Font.PLAIN, 28));
|
||||
return originalImage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import mobi.librera.smartreflow.model.Column;
|
||||
import mobi.librera.smartreflow.model.Line;
|
||||
import mobi.librera.smartreflow.model.Word;
|
||||
|
||||
public class SmartReflow implements SmartReflowInterface {
|
||||
|
||||
public static final int MIN_WORD_SIZE = 5;
|
||||
public static final int MIN_LINE_HEIGHT = 10;
|
||||
|
||||
public static int PADDING = 15;
|
||||
|
||||
public boolean isDrawColums;
|
||||
public boolean isDrawLines;
|
||||
public boolean isDrawWords;
|
||||
public boolean isDrawWordsOffsetLeft;
|
||||
public boolean isDrawChars;
|
||||
public boolean isDrawResultUsingWords = true;
|
||||
float averageTop = 0;
|
||||
int averageTopCount = 0;
|
||||
List<Column> columns = new ArrayList<>();
|
||||
List<Line> lines = new ArrayList<>();
|
||||
List<Word> words = new ArrayList<Word>();
|
||||
List<Word> wordsLong = new ArrayList<Word>();
|
||||
boolean isDrawResult = true;
|
||||
|
||||
PlatformImage image;
|
||||
|
||||
public SmartReflow(PlatformImage image) throws Exception {
|
||||
this.image = image;
|
||||
process(image);
|
||||
}
|
||||
|
||||
public boolean isMultyColumn() {
|
||||
return columns.size() >= 2;
|
||||
}
|
||||
|
||||
|
||||
public List<String> getStatistics() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("Columns :" + columns.size());
|
||||
list.add("Lines :" + lines.size());
|
||||
list.add("Words :" + words.size());
|
||||
list.add("Padding :" + PADDING);
|
||||
int sum = 0;
|
||||
for (Word w : wordsLong) {
|
||||
sum += w.offsetLeft;
|
||||
}
|
||||
list.add("Mid offset :" + sum / wordsLong.size());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void process(PlatformImage img) throws Exception {
|
||||
|
||||
ImageUtils.log("img", "w", img.getWidth(), "h", img.getHeight());
|
||||
PADDING = img.getWidth() / 200;
|
||||
System.out.println("PADDING " + PADDING + " k= " + (int) img.getWidth() / 15);
|
||||
|
||||
columns.clear();
|
||||
lines.clear();
|
||||
words.clear();
|
||||
|
||||
//detect colums
|
||||
boolean isColumn = true;
|
||||
|
||||
int center = img.getWidth() / 2;
|
||||
for (int dx = -PADDING / 2; dx < PADDING / 2; dx++) {
|
||||
for (int y = 0; y < img.getHeight(); y++) {
|
||||
if (img.isBlackPixel(center + dx, y)) {
|
||||
isColumn = false;
|
||||
}
|
||||
}
|
||||
if (isColumn) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Column column = null;
|
||||
int number = 0;
|
||||
|
||||
for (int x = 0; x < img.getWidth(); x++) {
|
||||
isColumn = false;
|
||||
for (int y = 0; y < img.getHeight(); y++) {
|
||||
if (img.isBlackPixel(x, y)) {
|
||||
isColumn = true;
|
||||
}
|
||||
if (isColumn && column == null) {
|
||||
column = new Column();
|
||||
column.number = number++;
|
||||
column.x1 = x;
|
||||
column.y1 = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (column != null && !isColumn) {
|
||||
column.x2 = x;
|
||||
column.y2 = img.getHeight() - 1;
|
||||
columns.add(column);
|
||||
column = null;
|
||||
}
|
||||
}
|
||||
System.out.println("Columns size" + columns.size());
|
||||
|
||||
for (Column col : columns) {
|
||||
// while (isLineWhite(col.y1, col.x1, col.x2)) {
|
||||
// col.y1++;
|
||||
// }
|
||||
//
|
||||
// while (isLineWhite(col.y2, col.x1, col.x2)) {
|
||||
// col.y2--;
|
||||
// }
|
||||
}
|
||||
|
||||
for (Column col : columns) {
|
||||
proccessColumn(img, col);
|
||||
}
|
||||
|
||||
|
||||
Word word = null;
|
||||
|
||||
Word prevCh = new Word();
|
||||
for (Word ch : words) {
|
||||
if (ch.isFirstWord || ch.offsetLeft > PADDING) {
|
||||
if (word == null) {
|
||||
word = new Word();
|
||||
|
||||
word.isFirstWord = ch.isFirstWord;
|
||||
word.offsetLeft = ch.offsetLeft;
|
||||
word.offsetTop = ch.offsetTop;
|
||||
|
||||
word.x1 = ch.x1;
|
||||
word.y1 = ch.y1;
|
||||
} else if (word != null) {
|
||||
word.y2 = prevCh.y2;
|
||||
word.x2 = prevCh.x2;
|
||||
|
||||
wordsLong.add(word);
|
||||
|
||||
word = new Word();
|
||||
word.x1 = ch.x1;
|
||||
word.y1 = ch.y1;
|
||||
|
||||
word.isFirstWord = ch.isFirstWord;
|
||||
word.offsetLeft = ch.offsetLeft;
|
||||
word.offsetTop = ch.offsetTop;
|
||||
|
||||
}
|
||||
} else if (ch.isLastWord) {
|
||||
word.y2 = ch.y2;
|
||||
word.x2 = ch.x2;
|
||||
wordsLong.add(word);
|
||||
word = null;
|
||||
}
|
||||
prevCh = ch;
|
||||
}
|
||||
if (word != null) {
|
||||
wordsLong.add(prevCh);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void proccessColumn(PlatformImage img, Column col) {
|
||||
averageTopCount = 0;
|
||||
averageTop = 0;
|
||||
|
||||
|
||||
//detect lines
|
||||
Line findLine = null;
|
||||
for (int y = col.y1; y < col.y2; y++) {
|
||||
|
||||
boolean isBank = isBlackHorizontal(img, y, col.x1, col.x2);
|
||||
|
||||
if (!isBank && findLine == null) {
|
||||
findLine = new Line();
|
||||
findLine.columnNumber = col.number;
|
||||
findLine.y1 = y;
|
||||
findLine.x1 = col.x1;
|
||||
} else if (isBank && findLine != null) {
|
||||
findLine.y2 = y;
|
||||
findLine.x2 = col.x2;
|
||||
if (findLine.height() > MIN_LINE_HEIGHT) {
|
||||
lines.add(findLine);
|
||||
findLine = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (findLine != null) {
|
||||
findLine.y2 = col.y2;
|
||||
findLine.x2 = col.x2;
|
||||
lines.add(findLine);
|
||||
}
|
||||
|
||||
//dect words
|
||||
Line prevLine = new Line();
|
||||
prevLine.y2 = col.y1;
|
||||
|
||||
for (Line line : lines) {
|
||||
if (line.columnNumber != col.number) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Word word = null;
|
||||
Word prevWord = new Word();
|
||||
prevWord.x2 = col.x1;
|
||||
|
||||
boolean isFirstWord = true;
|
||||
for (int x = col.x1; x < col.x2; x++) {
|
||||
boolean isBlank = isBlankVertical(img, x, line.y1, line.y2);
|
||||
|
||||
|
||||
if (!isBlank && word == null) {
|
||||
word = new Word();
|
||||
word.x1 = x;
|
||||
word.y1 = line.y1;
|
||||
|
||||
word.isFirstWord = isFirstWord;
|
||||
|
||||
|
||||
word.offsetLeft = word.x1 - prevWord.x2;
|
||||
word.offsetTop = line.y1 - prevLine.y2;
|
||||
|
||||
if (isFirstWord) {
|
||||
averageTop += word.offsetTop;
|
||||
averageTopCount++;
|
||||
}
|
||||
|
||||
isFirstWord = false;
|
||||
|
||||
} else if (isBlank && word != null) {
|
||||
word.x2 = x;
|
||||
word.y2 = line.y2;
|
||||
|
||||
if (word.width() > MIN_WORD_SIZE || word.height() > MIN_WORD_SIZE) {
|
||||
words.add(word);
|
||||
prevWord = word;
|
||||
}
|
||||
word = null;
|
||||
}
|
||||
}
|
||||
if (word != null) {
|
||||
word.x2 = col.x2;
|
||||
word.y2 = line.y2;
|
||||
words.add(word);
|
||||
}
|
||||
words.get(words.size() - 1).isLastWord = true;
|
||||
prevLine = line;
|
||||
}
|
||||
averageTop = averageTop / averageTopCount;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void drawObjects(PlatformImage output) {
|
||||
output.create(image.getWidth(), image.getHeight());
|
||||
|
||||
ImageUtils.log("Columns:", columns.size());
|
||||
ImageUtils.log("Lines:", lines.size());
|
||||
ImageUtils.log("Words:", words.size());
|
||||
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
output.setPixel(x, y, image.getPixel(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
if (isDrawColums) {
|
||||
for (Column col : columns) {
|
||||
ImageUtils.drawRect(output, col.x1, col.y1, col.x2, col.y2, PlatformImage.GREEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDrawLines) {
|
||||
for (Line l : lines) {
|
||||
ImageUtils.drawRect(output, l.x1, l.y1, l.x2, l.y2, PlatformImage.RED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (Word w : words) {
|
||||
if (isDrawChars) {
|
||||
ImageUtils.drawRect(output, w.x1, w.y1, w.x2, w.y2, PlatformImage.BLUE);
|
||||
}
|
||||
int dy = w.y1;
|
||||
ImageUtils.drawRect(output, w.x1 - w.offsetLeft, dy, w.x1, dy + 2, PlatformImage.YELLOW);
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (Word w : wordsLong) {
|
||||
ImageUtils.drawRect(output, w.x1, w.y1, w.x2, w.y2, PlatformImage.MAROON);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reflow(PlatformImage des) {
|
||||
if (columns.isEmpty()) {
|
||||
des.create(image.getWidth(), image.getHeight());
|
||||
for (int x = 0; x < des.getWidth(); x++) {
|
||||
for (int y = 0; y < des.getHeight(); y++) {
|
||||
des.setPixel(x, y, image.getPixel(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ImageUtils.setBackgroundColor(des, PlatformImage.WHITE);
|
||||
|
||||
|
||||
int dx = PADDING;
|
||||
int dy = 0;
|
||||
|
||||
Word prev = new Word();
|
||||
|
||||
List<Word> input;
|
||||
|
||||
|
||||
boolean isDrawResult = true;
|
||||
//if (isDrawResultUsingWords) {
|
||||
if (isDrawResultUsingWords) {
|
||||
input = wordsLong;
|
||||
} else {
|
||||
input = words;
|
||||
}
|
||||
for (Word w : input) {
|
||||
if (w.height() > des.getHeight()) {
|
||||
//w.y2 = w.y2 / 2;
|
||||
}
|
||||
if (w.width() > des.getWidth()) {
|
||||
//w.x2 = w.x2
|
||||
}
|
||||
dx = dx + w.offsetLeft;
|
||||
|
||||
if (w.height() != prev.height() && prev.height() > 0) {
|
||||
dy -= w.height() - prev.height();
|
||||
}
|
||||
|
||||
//chapter
|
||||
if (w.isFirstWord) {
|
||||
//w.offsetTop = PADDING;
|
||||
//ImageUtils.log("w.offsetLeft", w.offsetLeft);
|
||||
}
|
||||
if (w.isFirstWord && (w.offsetLeft > PADDING * 2 || w.offsetTop > PADDING + 5)) {
|
||||
dx = w.offsetLeft + PADDING;
|
||||
dy += w.height() + w.offsetTop;
|
||||
} else if (w.isFirstWord) {
|
||||
dx += Math.max(w.offsetLeft, PADDING);
|
||||
}
|
||||
|
||||
//new line
|
||||
if (w.width() + dx > des.getWidth() - PADDING) {
|
||||
dx = PADDING;
|
||||
dy += w.height() + averageTop;
|
||||
}
|
||||
|
||||
try {
|
||||
drawWordAt(w, dx, dy, image, des);
|
||||
dx += w.width();
|
||||
prev = w;
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
//continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void drawWordAt(Word w, int dx, int dy, PlatformImage input, PlatformImage output) {
|
||||
for (int i = w.x1; i < w.x2; i++) {
|
||||
for (int j = w.y1; j < w.y2; j++) {
|
||||
int pixel[] = input.getPixel(i, j);
|
||||
final int x = dx + (i - w.x1);
|
||||
final int y = dy + (j - w.y1);
|
||||
|
||||
output.setPixel(x, y, pixel);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public boolean isBlackHorizontal(PlatformImage img, int y, int x1, int x2) {
|
||||
boolean isBlank = true;
|
||||
|
||||
for (int x = x1; x < x2; x++) {
|
||||
|
||||
if (img.isBlackPixel(x, y)) {
|
||||
isBlank = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isBlank;
|
||||
}
|
||||
|
||||
|
||||
public boolean isBlankVertical(PlatformImage img, int x, int y1, int y2) {
|
||||
boolean isBlank = true;
|
||||
for (int y = y1; y < y2; y++) {
|
||||
|
||||
if (img.isBlackPixel(x, y)) {
|
||||
isBlank = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isBlank;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import mobi.librera.smartreflow.model.Rect;
|
||||
import mobi.librera.smartreflow.model.Word;
|
||||
|
||||
public class SmartReflow1 implements SmartReflowInterface {
|
||||
|
||||
|
||||
PlatformImage img;
|
||||
List<Rect> lines = new ArrayList<Rect>();
|
||||
List<Word> words = new ArrayList<Word>();
|
||||
|
||||
int minLineHeight = 5;
|
||||
int minSpaceSize = 5;
|
||||
boolean isTwoColumns = true;
|
||||
|
||||
@Override
|
||||
public void process(PlatformImage img) throws Exception {
|
||||
this.img = img;
|
||||
|
||||
lines.clear();
|
||||
words.clear();
|
||||
|
||||
|
||||
int center = img.getWidth() / 2;
|
||||
for (int dx = -minSpaceSize; dx < minSpaceSize; dx++) {
|
||||
for (int y = 0; y < img.getHeight(); y++) {
|
||||
if (img.isBlackPixel(center + dx, y)) {
|
||||
isTwoColumns = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isTwoColumns) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isTwoColumns) {
|
||||
lines.addAll(ImageUtils.splitHorizontal(img, new Rect(0, 0, img.getWidth() / 2, img.getHeight() - 1), minLineHeight));
|
||||
lines.addAll(ImageUtils.splitHorizontal(img, new Rect(img.getWidth() / 2, 0, img.getWidth() - 1, img.getHeight() - 1), minLineHeight));
|
||||
} else {
|
||||
lines.addAll(ImageUtils.splitHorizontal(img, new Rect(0, 0, img.getWidth() - 1, img.getHeight() - 1), minLineHeight));
|
||||
}
|
||||
|
||||
|
||||
//accurate lines
|
||||
for (Rect l : lines) {
|
||||
ImageUtils.removeWhiteBegin(img, l);
|
||||
ImageUtils.removeWhiteEnd(img, l);
|
||||
}
|
||||
|
||||
for (Rect l : lines) {
|
||||
final List<Word> all = ImageUtils.splitVertical(img, l, minSpaceSize);
|
||||
if (!all.isEmpty()) {
|
||||
final Word first = all.get(0);
|
||||
|
||||
first.isFirstWord = true;
|
||||
}
|
||||
words.addAll(all);
|
||||
}
|
||||
|
||||
for (Rect l : words) {
|
||||
ImageUtils.removeWhiteBegin(img, l);
|
||||
ImageUtils.removeWhiteEnd(img, l);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawObjects(PlatformImage out) {
|
||||
out.create(img.getWidth(), img.getHeight());
|
||||
ImageUtils.copyRect(img, out, 0, 0, img.getWidth(), img.getHeight());
|
||||
for (Rect r : lines) {
|
||||
ImageUtils.drawRect(out, r.x1, r.y1, r.x2, r.y2, PlatformImage.YELLOW);
|
||||
}
|
||||
for (Rect r : words) {
|
||||
ImageUtils.drawRect(out, r.x1, r.y1, r.x2, r.y2, PlatformImage.BLUE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reflow(PlatformImage out) {
|
||||
if (isTwoColumns) {
|
||||
//out.create(img.getWidth() / 2, img.getHeight() * 2);
|
||||
} else {
|
||||
//out.create(img.getWidth() / 2, img.getHeight());
|
||||
}
|
||||
|
||||
ImageUtils.setBackgroundColor(out, PlatformImage.WHITE);
|
||||
|
||||
|
||||
Word prev = words.get(0);
|
||||
|
||||
int padding = 10;
|
||||
int indent = 5;
|
||||
int space = 10;
|
||||
|
||||
|
||||
int dx = padding;
|
||||
int dy = padding;
|
||||
|
||||
for (Word w : words) {
|
||||
|
||||
if (w.isFirstWord) {
|
||||
int dh = w.y1 - prev.y2;
|
||||
|
||||
if (dh > prev.height() ) {
|
||||
dy += dh + prev.height();
|
||||
dx = padding;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Math.abs(w.height() - prev.height()) > prev.height() / 2) {
|
||||
dx = padding;
|
||||
dy += prev.height();
|
||||
}
|
||||
|
||||
if (dx + w.width() + padding > out.getWidth()) {
|
||||
dx = padding;
|
||||
dy += prev.height() + indent;
|
||||
}
|
||||
if (dy + w.height() + padding > out.getHeight()) {
|
||||
break;
|
||||
}
|
||||
|
||||
ImageUtils.drawWord(w, dx, dy, img, out);
|
||||
dx = dx + w.width() + space;
|
||||
prev = w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getStatistics() {
|
||||
return Arrays.asList("Lines: " + lines.size());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SmartReflowInterface {
|
||||
|
||||
|
||||
void process(PlatformImage img) throws Exception;
|
||||
|
||||
void drawObjects(PlatformImage output);
|
||||
|
||||
void reflow(PlatformImage des);
|
||||
|
||||
List<String> getStatistics();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package mobi.librera.smartreflow;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
public class TestImage {
|
||||
|
||||
static String img = "/home/ivan-dev/IdeaProjects/SmartReflow/images/sample6.png";
|
||||
|
||||
|
||||
static GraphicsConfiguration gc;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final JFrame frame = new JFrame(gc);
|
||||
|
||||
final AwtPlatformImage input = new AwtPlatformImage(img);
|
||||
|
||||
final AwtPlatformImage output = new AwtPlatformImage(600,1000);
|
||||
|
||||
SmartReflow sm = new SmartReflow(input);
|
||||
sm.reflow(output);
|
||||
|
||||
|
||||
frame.add(new JLabel(new ImageIcon(output.getImage())));
|
||||
|
||||
|
||||
frame.setTitle("Librera Native Reflow");
|
||||
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
|
||||
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
frame.setSize((int) screenSize.getWidth(), (int) screenSize.getHeight());
|
||||
frame.setVisible(true);
|
||||
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setResizable(true);
|
||||
|
||||
frame.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent ke) { // handler
|
||||
if (ke.getKeyCode() == ke.VK_ESCAPE) {
|
||||
frame.dispose();
|
||||
} else {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void test1(PlatformImage image, PlatformImage output) throws Exception {
|
||||
|
||||
image.create(200, 200);
|
||||
//image.load(img);
|
||||
|
||||
|
||||
ImageUtils.setBackgroundColor(image, PlatformImage.WHITE);
|
||||
for (int x = 0; x < 200; x++) {
|
||||
image.setPixel(x, x, PlatformImage.RED);
|
||||
}
|
||||
ImageUtils.drawRect(image, 10, 10, 100, 100, PlatformImage.BLUE);
|
||||
|
||||
|
||||
output.create(400, 400);
|
||||
|
||||
ImageUtils.copyRect(image, output, 0, 0, 150, 150);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package mobi.librera.smartreflow.model;
|
||||
|
||||
public class Column extends Rect {
|
||||
public int number;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package mobi.librera.smartreflow.model;
|
||||
|
||||
public class Line extends Rect {
|
||||
|
||||
public int columnNumber;
|
||||
|
||||
public Line() {
|
||||
|
||||
}
|
||||
|
||||
public Line(int x, int y) {
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
public Line(int x, int y, int x1, int y1) {
|
||||
super(x, y, x1, y1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mobi.librera.smartreflow.model;
|
||||
|
||||
public class Rect {
|
||||
public int x1;
|
||||
public int y1;
|
||||
public int x2;
|
||||
public int y2;
|
||||
|
||||
public Rect() {
|
||||
|
||||
}
|
||||
|
||||
public Rect(int x, int y) {
|
||||
this.x1 = x;
|
||||
this.y1 = y;
|
||||
}
|
||||
|
||||
public Rect(int x1, int y1, int x2, int y2) {
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return x2 - x1;
|
||||
}
|
||||
|
||||
public int height() {
|
||||
return y2 - y1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package mobi.librera.smartreflow.model;
|
||||
|
||||
public class Word extends Rect {
|
||||
public int offsetLeft;
|
||||
public int offsetTop;
|
||||
public boolean isFirstWord;
|
||||
public boolean isLastWord;
|
||||
|
||||
public Word() {
|
||||
}
|
||||
|
||||
public Word(int x, int y) {
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user