2010-01-27 3 views
5

Мне интересно, есть ли «умный» способ разделения изображения на основе определенных функций.Как отделить изображение от двух с java

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

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

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

В результате раскола было бы что-то вроде этого:

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

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

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

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

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

Буду признателен, если я получу несколько идей для более эффективных способов выполнения этой задачи. Я хочу использовать встроенные библиотеки 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")); 
} 
+0

Примечание: после обнаружения правого края можно просто вырваться из цикла for. – Kiril

ответ

5

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

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

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

цв среды:

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

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

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

+0

+1: Я думаю, что это будет работать очень хорошо и очень просто. –

+0

Я, это то, что я закончил делать. Я отправлю код другим пользователям. – Kiril

1

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

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

+0

@trashgod Хорошая точка ... Так что BufferedImage и Graphics2D лучше всего использовать в java-библиотеке (я не хочу выходить на внешний уровень)? – Kiril

+0

Да; Я добавил некоторые подробности выше. – trashgod

1

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

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

EDIT: Г-н Берна отметил, что это не будет работать с вогнутыми объектами.

+0

Это ломается, когда капля вогнута. Если верхняя часть пятна была сформирована как пальцы, вы могли бы получить белый -> черный -> белый переход, не найдя края первого капли. –

+0

@Mr. Берна: Верно. Он будет работать (надежно) только для выпуклых капель. –

1

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

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

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

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

+0

Мне нравится идея сканирования горизонтальной линии ... Я буду экспериментировать с ней.В моем примере с 1000 изображениями у меня не было изображения, где части двух капель занимают одну и ту же вертикальную линию, капли всегда разделены несколькими белыми линиями. – Kiril

Смежные вопросы