Алгоритм изображения для получения вертикальных или горизонтальных линий - PullRequest
0 голосов
/ 04 июня 2019

Я действительно ценю все ваши усилия, но, к сожалению, они не отвечают на вопрос.Я изложил подход bruteForce выше, и мой вопрос заключается в том, как я могу оптимизировать его.Вот краткий обзор моего подхода грубой силы:

Я хочу получить как можно меньше вертикальных и горизонтальных линий на изображении.Моя идея состояла в том, чтобы получить самую длинную линию (вертикальную или горизонтальную), а затем вторую самую длинную и т. Д., Что (я думаю) решает эту проблему.

Эти изображения будут выглядеть как 8-битные изображения, напримерэто, но в более широком и гораздо более детальном виде: Mario 8Bit

До сих пор я придумал итерировать все изображение целиком, извлечь самую длинную строку, сохранить ее и повторить изображение, но избегаяпосетить линию, которая уже была посещена.Если больше не будет посещаемых строк, он остановится.

Поскольку это грубое обращение, это занимает больше времени, чем мне нужно.И я ищу способы оптимизировать это с точки зрения всего подхода, а не только крошечных оптимизаций внутри кода.

Ответы [ 2 ]

0 голосов
/ 04 июня 2019

Из описания кажется, что вы ищете линии, состоящие из пикселей, которые имеют точно того же цвета, и которые идеально горизонтальны или вертикальны.

Я создал пример, который генерирует несколько случайных красных линий на изображении:

LinesInImage

Синяя подсветка указывает на найденные линии, а зеленая подсветка показывает самую длинную найденную линию.

Линии обнаруживаются простым способом:

  • Пройдите по всем пикселям (x, y), поканайден пиксель с цветом линии
  • Проверьте соседние пиксели.То есть проверьте, имеют ли пиксели «север», «юг», «восток» и «запад» текущего пикселя тот же цвет, что и текущий

    • Если текущий пикселькрасный, и пиксель запад его белого цвета, а пиксель восток его красного цвета, тогда в этой точке начинается линия горизонтальная .В основном это выглядит для этого шаблона:

      . ? .
      W R R
      . ? .
      
    • Если текущий пиксель красный, а пиксель север его белый, а пиксель юг красного цвета, затем в этой точке начинается вертикальная линия.По сути, это выглядит следующим образом:

      . W .
      ? R ?
      . R .
      
  • При обнаружении такого начала линии, проходите горизонтально или вертикально, пока не будет найден конец линии,и сохраните полученную строку

  • Наконец, сортируйте строки по их длине

Обратите внимание, что здесь есть некоторые предположения и угловые случаи.Хотя находит приемлемые результаты, все становится непросто, когда две линии расположены рядом друг с другом.Проще говоря: если у вас есть образец красных пикселей, как этот ...

. . . . .
. R R R .
. R R R .
. R R R .
. . . . .

, то, строго говоря, невозможно сказать, являются ли они

  • три горизонтальные линии длиной 3
  • три вертикальные линии длиной 3
  • три горизонтальные линии длиной 2 и одна вертикальная линия длиной 3
  • или что угодно ...

Однако предлагаемый подход показан в этом MCVE:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class FindLinesInImage {
    public static void main(String[] args) {

        int w = 100;
        int h = 100;

        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

        int numLines = 30;
        int lineRgb = Color.RED.getRGB();

        Random random = new Random(0);
        drawSomeLines(image, numLines, lineRgb, random);

        List<Line2D> lines = findLines(image, lineRgb);
        Comparator<Line2D> comparator = (line0, line1) -> {
            double length0 = line0.getP1().distance(line0.getP2());
            double length1 = line1.getP1().distance(line1.getP2());
            return Double.compare(length1, length0);
        };
        Collections.sort(lines, comparator);

        SwingUtilities.invokeLater(() -> createAndShowGui(image, lines));
    }

    private static void createAndShowGui(BufferedImage image, List<Line2D> lines) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        double scaling = 5.0;
        AffineTransform lineTransform = AffineTransform.getScaleInstance(scaling, scaling);
        lineTransform.translate(0.5, 0.5);
        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics gr) {
                super.paintComponent(gr);
                Graphics2D g = (Graphics2D) gr;
                AffineTransform oldAt = g.getTransform();
                g.scale(scaling, scaling);
                g.drawImage(image, 0, 0, null);
                g.setTransform(oldAt);
                g.setColor(new Color(0, 128, 0, 64));
                g.setStroke(new BasicStroke((float) (3 * scaling)));
                for (int i = 0; i < lines.size(); i++) {
                    Line2D line = lines.get(i);
                    g.draw(lineTransform.createTransformedShape(line));
                    g.setColor(new Color(0, 0, 255, 64));
                }

            };
        };
        f.getContentPane().add(panel);

        f.setSize(800, 800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static List<Line2D> findLines(BufferedImage image, int rgb) {
        List<Line2D> lines = new ArrayList<Line2D>();
        int w = image.getWidth();
        int h = image.getHeight();
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {

                boolean atC = pixelHasColor(image, x, y, rgb);
                boolean atN = pixelHasColor(image, x, y - 1, rgb);
                boolean atS = pixelHasColor(image, x, y + 1, rgb);
                boolean atE = pixelHasColor(image, x + 1, y, rgb);
                boolean atW = pixelHasColor(image, x - 1, y, rgb);

                if (atC) {
                    if (atE && !atW) {
                        Line2D line = computeLine(image, x, y, 1, 0, rgb);
                        System.out.println("Line " + line.getP1() + " " + line.getP2());
                        lines.add(line);
                    }
                    if (atS && !atN) {
                        Line2D line = computeLine(image, x, y, 0, 1, rgb);
                        System.out.println("Line " + line.getP1() + " " + line.getP2());
                        lines.add(line);
                    }
                    if (!atS && !atN & !atW && !atE) {
                        Line2D line = new Line2D.Double(x, y, x, y);
                        lines.add(line);
                    }
                }
            }
        }
        return lines;
    }

    private static Line2D computeLine(BufferedImage image, int x, int y, int dx, int dy, int rgb) {
        int cx = x;
        int cy = y;
        while (pixelHasColor(image, cx, cy, rgb)) {
            cx += dx;
            cy += dy;
        }
        return new Line2D.Double(x, y, cx - dx, cy - dy);
    }

    private static boolean pixelHasColor(BufferedImage image, int x, int y, int rgb) {
        if (x < 0 || y < 0) {
            return false;
        }
        int w = image.getWidth();
        int h = image.getHeight();
        if (x >= w || y >= h) {
            return false;
        }
        return image.getRGB(x, y) == rgb;
    }

    private static void drawSomeLines(BufferedImage image, int n, int rgb, Random random) {
        int w = image.getWidth();
        int h = image.getHeight();
        Graphics2D g = image.createGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, w, h);
        g.setColor(new Color(rgb));
        for (int i = 0; i < n; i++) {
            int x0 = random.nextInt(w / 2) * 2;
            int y0 = random.nextInt(h / 2) * 2;
            int x1 = x0;
            int y1 = y0;
            boolean horizontal = random.nextBoolean();
            if (horizontal) {
                x1 = x0 + random.nextInt(w - x0);
            } else {
                y1 = y0 + random.nextInt(h - y0);
            }
            g.drawLine(x0, y0, x1, y1);
        }
        g.dispose();
    }

    private static void drawLine(BufferedImage image, int x0, int y0, int x1, int y1, int rgb) {
        Graphics2D g = image.createGraphics();
        g.setColor(new Color(rgb));
        g.drawLine(x0, y0, x1, y1);
        g.dispose();
    }

}
0 голосов
/ 04 июня 2019

Вот пример псевдокода для поиска всех горизонталей:

List lineList;

for(v : [0, imageHeight-1]) {
  int streakStart = 0;
  Color lastColor = colorAt(0, v);
  for(u : [1, imageWidth-1]) {
    Color currentColor = colorAt(u, v);
    if(currentColr != lastColor) {
      lineList.insert(Line(streakStart, u-1));
      streakStart = u;
      lastColor = currentColor;
    }
  }
  lineList.insert(streakStart, imageWidth-1);
}

Вертикальные линии вы получаете одинаково, переключая u и v.

...