Обрезать изображения с динамической подписью

Я разрабатываю Java-приложение, которое захватывает подписи клиентов с USB-планшета Wacom, создает белое фоновое изображение размером 320x200 пикселей с подписью и сохраняет его в базе данных, чтобы позже распечатать его в формате PDF.

Прежде чем сохранять их, я хотел бы обрезать эти изображения поверх подписи. Я имею в виду, что один клиент может подписывать в левой зоне планшета, а другой — в правой зоне. Таким образом, все подписи будут иметь разные позиции в изображении подписи и бесполезное пустое пространство. Например, различные изображения подписи, и мне нужно что-то вроде это

Итак, мой вопрос, возможно ли это? Есть ли возможность динамически обрезать эти изображения по подписи перед их сохранением? Зная, что все подписи имеют разный размер, невозможно всегда обрезать их с одних и тех же позиций. Или было бы лучше, например, распечатать прямоугольник в планшете, заставляя клиентов расписываться внутри него? (Не знаю, возможно ли это, я полагаю, да).

Спасибо.


person Roms    schedule 31.01.2018    source источник
comment
Вы можете начать с анализа изображения попиксельно. Создайте пустой Rectangle. Для каждого пикселя изображения (порядок итерации не имеет значения) добавьте эту координату к прямоугольнику, если он не белый. Это, кстати, идеально распараллеливается с потоками Java 8. Мне нравится этот вызов, поэтому, если вы можете подождать ~ 6 часов, я могу приготовить пример дома.   -  person Mark Jeronimus    schedule 31.01.2018
comment
Конечно, @MarkJeronimus был бы признателен. Мне нравится твоя идея, моя была чем-то похожа на твою, но даже не знал с чего начать. И не беспокойтесь, я могу ждать сколько угодно :) Спасибо!   -  person Roms    schedule 31.01.2018
comment
Какой язык программирования вы используете? x0=10000,y0=10000;x1=0,y1=0 перебрать все пиксели (x,y) в изображении, и если установлен пиксель, сделать x1=max(x,x1); у1=макс(у,у1); х0=мин(х0,х); у0=мин(у0;у); после итерации у вас есть прямоугольник от x0,y0 до y1,y1 для обрезки изображения.   -  person TomB    schedule 01.02.2018
comment
@TomB Привет, Том, я использую Java. Я проверю ваш ответ, спасибо :)   -  person Roms    schedule 01.02.2018
comment
Если вам нужно готовое программное обеспечение, которое делает именно то, что вы пытаетесь сделать (поддерживает wacom, создает подписанные PDF-файлы и т. д.), не стесняйтесь заглянуть на www.is2.de/insign (мы создатели inSign).   -  person TomB    schedule 01.02.2018


Ответы (2)


Я обещал подход с параллельным потоком. Вот.

Необрезанное изображение (png): http://assets-cdn.github.com/images/modules/open_graph/github-mark.png

public static void main(String[] args) throws Exception {
    BufferedImage img = ImageIO.read(new URL("http://assets-cdn.github.com/images/modules/open_graph/github-mark.png"));

    BufferedImage sigImage = cropSignature(img);

    SwingUtilities.invokeLater(() -> {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.setContentPane(new JLabel(new ImageIcon(sigImage)));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    });
}

private static BufferedImage cropSignature(BufferedImage img) {
    // Iterating over height has better CPU cache performance.
    Rectangle sigRect = IntStream.range(0, img.getHeight())
                                 .parallel()
                                 .mapToObj(y -> getHorizontalSpan(img, y))
                                 .collect(() -> new Rectangle(0, 0, -1, -1),
                                          (r1, r2) -> r1.add(r2),
                                          (r1, r2) -> r1.add(r2));

    return img.getSubimage(sigRect.x, sigRect.y, sigRect.width, sigRect.height);
}

private static Rectangle getHorizontalSpan(BufferedImage img, int y) {
    // Don't parallelize. The 'combiner' is therefore not necessary and may be null.
    return IntStream.range(0, img.getWidth())
                    .filter(x -> isSignatureColor(img.getRGB(x, y)))
                    .collect(() -> new Rectangle(0, 0, -1, -1),
                             (r, x) -> r.add(x, y),
                             null);
}

private static boolean isSignatureColor(int rgb) {
    // Easiest criteria, but fails on JPG files because it doesn't use a tolerance. 
    return rgb != -1;
}
person Mark Jeronimus    schedule 02.02.2018

person    schedule
comment
Спасибо! Я адаптировал ваш код к своей программе и работает как шарм! - person Roms; 01.02.2018