Как разделить изображение на два с помощью Java - PullRequest
5 голосов
/ 27 января 2010

Мне интересно, существует ли "умный" способ разделения изображения на основе определенных функций.

Изображения размером 300x57, черно-белые (на самом деле это оттенки серого, но большинство цветов - черный или белый), они состоят из двух основных функций (назовем их каплями), разделенных черным пространством, каждая капля слегка изменяется по ширине и высота, положение капель также меняется, капли НИКОГДА не перекрываются!

Вот как «выглядит» изображение:

-------------------------
----WWW---------WWWWW----
---WWWWWWW----WWWWWW-----
-----WWWW-------WWW------
-------------------------

Получившийся сплит будет выглядеть примерно так:

------------     -------------
----WWW-----     ----WWWWW----
---WWWWWWW--     --WWWWWW-----
-----WWWW---     ----WWW------
------------     -------------

Шаги, которые я планирую предпринять, чтобы разделить изображение:

  1. Сканирование изображения с одной стороны на другую.
  2. Определить края капель.
  3. Возьмите расстояние между двумя внутренними краями.
  4. Разделить изображение на середину внутреннего расстояния.
  5. Сохраните два изображения в виде отдельных файлов.

Было бы неплохо, если бы я нормализовал ширину изображения, чтобы при сохранении все мои изображения имели одинаковую ширину.

У меня нет опыта работы с изображениями, поэтому я не знаю, как это эффективно сделать. В настоящее время я использую BufferedImage, получаю ширину / высоту, перебираю каждый пиксель и т. Д. Для моей проблемы нет неправильного решения, но я ищу более эффективное (меньше кода + быстрее). Я также изучал java.awt. Графика ...

Буду признателен, если у меня появятся идеи для более эффективных способов решения этой задачи. Я хочу придерживаться встроенных библиотек Java, поэтому BufferedImage или Graphics2D наиболее эффективная вещь для использования в этом случае?

EDIT: Вот код после прочтения предложений:

public void splitAndSaveImage( BufferedImage image ) throws IOException
{
    // Process image ------------------------------------------         
    int height = image.getHeight();
    int width = image.getWidth();
    boolean edgeDetected = false;
    double averageColor = 0;
    int threshold = -10;
    int rightEdge = 0;
    int leftEdge = 0;
    int middle = 0;

    // Scan the image and determine the edges of the blobs.
    for(int w = 0; w < width; ++w)
    {               
        for(int h = 0; h < height; ++h)
        {
            averageColor += image.getRGB(w, h);
        }

        averageColor = Math.round(averageColor/(double)height);

        if( averageColor /*!=-1*/< threshold && !edgeDetected )
        {
            // Detected the beginning of the right blob
            edgeDetected = true;
            rightEdge = w;
        }else if( averageColor >= threshold && edgeDetected )
        {
            // Detected the end of the left blob
            edgeDetected = false;
            leftEdge = leftEdge==0? w:leftEdge;
        }

        averageColor = 0;
    }

    // Split the image at the middle of the inside distance.
    middle = (leftEdge + rightEdge)/2;

    // Crop the image
    BufferedImage leftImage = image.getSubimage(0, 0, middle, height);

    BufferedImage rightImage = image.getSubimage(middle, 0, (width-middle), height);

    // Save the image
    // Save to file -------------------------------------------
    ImageIO.write(leftImage, "jpeg", new File("leftImage.jpeg"));

    ImageIO.write(rightImage, "jpeg", new File("rightImage.jpeg"));
}

Ответы [ 4 ]

5 голосов
/ 29 января 2010

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

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

-------------------------
----WWW---------WWWWW----
---WWWWWWW----WWWWWW-----
-----WWWW-------WWW------
-------------------------

цв ср:

---wwWWwww-----wWWWWww---

В зависимости от того, насколько пустым является пространство (в пикселях) между двумя объектами, вы можете установить пороговое значение довольно низким. Если есть шум, он должен быть немного выше.

Найти правильное пороговое значение может быть непросто, если только вы не можете определить его алгоритмически.

1 голос
/ 28 января 2010

Я не думаю, что есть какая-либо причина, кроме как сканировать каждую строку и останавливаться, когда вы получили переход белый -> черный -> белый (не нужно сканировать всю строку!). Если вы можете сделать какие-либо предположения о положении капель, вы могли бы немного уточнить его, выбрав начальную точку в середине изображения, а затем выполнив поиск слева и справа оттуда. Но я серьезно сомневаюсь, что это стоило бы усилий.

Также нет необходимости сначала запускать алгоритм обнаружения краев на изображении. Просто отсканируйте линии!

РЕДАКТИРОВАТЬ: г-н Берна указал, что это не будет работать с вогнутыми объектами.

1 голос
/ 29 января 2010

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

Если эти проверки не пройдены, потребуется отсканировать дополнительные линии, но для всех правильно сформированных изображений, в которых пятна расположены по центру в правой и левой половинах изображения, этот метод будет выполнять только два сканирования строк. Этот метод может занять больше времени для других изображений или даже прерваться для изображений с краями. Это сломалось бы для этого примера:

-------------------------
----WWW----WWWWWWWWWW----
---WWWWWWW----WWWWWW-----
-----WWWWWWWW---WWW------
-------------------------

Но вопрос, похоже, указывает на то, что эта ситуация невозможна. Если причина разделения изображения требует обработки каждого изображения, вам понадобится метод отступления. Вам не понадобится метод отступления, если крайние случаи могут быть отклонены. Как только сканирование обнаружит, что изображение выходит за допустимые пределы, вы можете прекратить проверку изображения. Например, если вертикальная полностью белая линия не может быть найдена в центральной трети изображения, вы можете отклонить изображение. Или вы можете просто использовать этот метод в качестве оптимизации, выполнив эту проверку на двух строках, чтобы найти и разделить правильно сформированные изображения, а затем передать плохо сформированные изображения более тщательному алгоритму.

1 голос
/ 27 января 2010

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

Добавление: если вы предпочитаете избегать внешних зависимостей, BufferedImage - хороший выбор. Как только вы определите края, метод getSubimage() будет удобен. Вы можете эффективно использовать один из методов Raster getPixels() в свертке. ImageIO можно записать результаты.

...