Gast commit!
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
@@ -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