Разделить изображение на части

Есть ли что-нибудь в приложении imagemagick или gimp или другом совместимом с Linux, что может автоматически обнаруживать отдельные объекты на изображении и возвращать какое-либо местоположение объекта или хранить каждый объект в виде отдельного изображения?

У меня есть образ этот:

enter image description here

Для других изображений, где объекты расположены на сетке, я успешно использовал оператор crop в imagemagick, например. для сетки 3x3:

convert -crop [email protected] in-image.jpg  out-image-%d.jpg

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

Ответ 1

Это можно сделать с помощью ImageMagick несколькими шагами. Изображение orignal называется meat.jpg:

convert meat.jpg -threshold 98% -morphology Dilate Octagon meat_0.png

meat_0.png

convert meat_0.png text: | grep -m 1 black

Это дает вам расположение пикселей в области первой части мяса:

131,11: (  0,  0,  0)  #000000  black

Мы будем использовать это, чтобы покрасить первую часть в красный цвет, отделить красный канал, а затем создать и применить маску для первой части:

 convert meat_0.png -fill red -bordercolor white \
         -draw 'color 131,11 filltoborder' meat_1_red.png
 convert meat_1_red.png -channel R -separate meat_1.png
 convert meat_1_red.png meat_1.png -compose subtract \
         -threshold 50% -composite -morphology Dilate Octagon \
         -negate meat_1_mask.png
 convert meat_1_mask.png meat.jpg -compose Screen -composite \
         -trim meat_1.jpg

Полученный результат meat_1.jpg уже обрезается. Затем вы можете продолжить то же самое с meat_1.png вместо meat_0.png, генерируя meat_2.png в качестве основы для последовательных итераций "на лету". Возможно, это можно упростить и обернуть в оболочку script.

meat_1.jpg

Ответ 2

Я бы занялся этим с помощью подхода "Connected Components Analysis" или "Image Segmentation", такого как...

Сначала разделите входное изображение на компоненты, указав минимальный размер (чтобы удалить меньшие куски) и разрешив 8-соединение (т.е. 8 соседних пикселей N, NE, E, SE, S, SW, W, NW считаются соседями), а не 4-связность, которая учитывает только N, E, S и W пикселей.

convert http://i.stack.imgur.com/T2VEJ.jpg -threshold 98% \
    -morphology dilate octagon                            \
    -define connected-components:area-threshold=800       \
    -define connected-components:verbose=true             \
    -connected-components 8 -auto-level PNG8:lumps.png

который дает этот результат:

Objects (id: bounding-box centroid area mean-color):
  0: 450x450+0+0 219.2,222.0 93240 srgb(255,255,255)
  14: 127x98+111+158 173.0,209.4 9295 srgb(0,0,0)
  29: 105x91+331+303 384.1,346.9 6205 srgb(0,0,0)
  8: 99x75+340+85 388.9,124.6 5817 srgb(1,1,1)
  15: 110x69+330+168 385.4,204.9 5640 srgb(1,1,1)
  3: 114x62+212+12 270.0,42.4 5021 srgb(0,0,0)
  4: 103x63+335+12 388.9,44.9 4783 srgb(0,0,0)
  11: 99x61+13+134 61.5,159.1 4181 srgb(0,0,0)
  37: 128x52+313+388 375.1,418.4 4058 srgb(0,0,0)
  24: 95x62+24+256 69.6,285.7 4017 srgb(0,0,0)
  2: 91x68+15+12 62.0,44.4 3965 srgb(0,0,0)
  38: 91x50+10+391 55.1,417.0 3884 srgb(0,0,0)
  12: 83x64+249+134 288.3,168.4 3761 srgb(0,0,0)
  19: 119x62+320+240 385.4,268.4 3695 srgb(9,9,9)
  25: 93x63+128+268 176.1,302.1 3612 srgb(0,0,0)
  39: 96x49+111+391 158.1,416.0 3610 srgb(0,0,0)
  31: 104x59+117+333 172.9,360.1 3493 srgb(0,0,0)
  33: 88x55+238+335 279.3,364.5 3440 srgb(0,0,0)
  26: 121x54+230+271 287.6,294.0 3431 srgb(8,8,8)
  1: 98x61+109+11 159.7,40.0 3355 srgb(0,0,0)
  40: 88x42+218+399 262.3,419.7 3321 srgb(0,0,0)
  6: 87x61+115+70 157.9,100.1 3263 srgb(0,0,0)
  30: 97x57+14+327 57.3,357.2 3237 srgb(55,55,55)
  17: 84x57+13+207 53.1,232.2 2995 srgb(0,0,0)
  5: 107x58+10+68 58.9,97.5 2988 srgb(0,0,0)
  18: 77x60+237+212 273.0,243.0 2862 srgb(0,0,0)
  7: 87x49+249+78 291.8,99.3 2703 srgb(9,9,9)
  10: 82x51+178+109 222.8,133.9 2628 srgb(0,0,0)

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

enter image description here

Выходное изображение называется lumps.png, и оно выглядит так:

enter image description here

и вы можете видеть, что каждый компонент (кусок мяса) имеет другой серый уровень, связанный с ним. Вы также можете проанализировать lumps.png и извлечь отдельную маску для каждого куска мяса, например:

#!/bin/bash
# Extract every grey level, and montage together all that are not entirely black
rm mask_*png 2> /dev/null
mask=0
for v in {1..255}; do
   ((l=v*255))
   ((h=l+255))
   mean=$(convert lumps.png -black-threshold "$l" -white-threshold "$h" -fill black -opaque white -threshold 1 -verbose info: | grep -c "mean: 0 ")
   if [ "$mean" -eq 0 ]; then
     convert lumps.png -black-threshold "$l" -white-threshold "$h" -fill black -opaque white -threshold 1 mask_$mask.png
     ((mask++))
   fi
done 

Это дает нам такие маски:

enter image description here

и этот

enter image description here

мы можем увидеть их все вместе, если мы сделаем это

montage -tile 4x mask_* montage_masks.png

enter image description here

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

seg=0
rm segment_*png 2> /dev/null
for f in mask_*png; do
   convert http://i.stack.imgur.com/T2VEJ.jpg $f -compose copy-opacity -composite -trim +repage segment_$seg.png
   ((seg++))
done

И они будут выглядеть так:

enter image description here

и этот

enter image description here

Или, мы можем собрать их все так:

montage -background white -tile 4x segment_* montage_results.png

enter image description here

Холодный вопрос: -)

Ответ 3

С GIMP вы можете сделать это следующим образом:

  1. Используйте Magic Wand (выберите инструмент "Смежные регионы") и выберите фон;
  2. Меню Select> Invert, чтобы получить несколько вариантов выбора;
  3. Установите script-fu export-selected-regions ниже, если у вас его еще нет;
  4. Меню Select> Export Selected Regions, ваши новые изображения должны находиться в той же папке, что и исходная.

Если вы используете GIMP 2.10 в mac, поместите приведенный ниже скрипт в: ~/Library/Application\ Support/GIMP/2.10/scripts/export-selected-regions.scm, проверьте папку, подходящую для другой системы.

;;; Non-interactively save all selected regions as separate files
(define (script-fu-export-selected-regions image drawable)
  ;; Start
  (gimp-image-undo-group-start image)

  ;; If there are selections
  (when (= 0 (car (gimp-selection-is-empty image)))
    (let ((number 1) (prefix "") (suffix ""))
      ;; Construct filename components
      (let* ((parts (strbreakup (car (gimp-image-get-filename image)) "."))
             (coextension (unbreakupstr (reverse (cdr (reverse parts))) "."))
             (extension (cadr parts)))
        (set! prefix (string-append coextension "_selection-" ))
        (set! suffix (string-append "." extension)))

      ;; Convert all selections to a single path
      (plug-in-sel2path RUN-NONINTERACTIVE image drawable)

      ;; For each stroke in the path
      (let ((vectors (vector-ref (cadr (gimp-image-get-vectors image)) 0)))
        (for-each (lambda (stroke)
                    ;; Convert the stroke back into a selection
                    (let ((buffer (car (gimp-vectors-new image "buffer")))
                          (points (gimp-vectors-stroke-get-points vectors stroke)))
                      (gimp-image-insert-vectors image buffer 0 -1)
                      (apply gimp-vectors-stroke-new-from-points buffer points)
                      (gimp-vectors-to-selection buffer 2 TRUE FALSE 0 0)
                      (gimp-image-remove-vectors image buffer))

                    ;; Replace the selection with its bounding box
                    (apply (lambda (x0 y0 x1 y1)
                             (gimp-image-select-rectangle image 2 x0 y0 (- x1 x0) (- y1 y0)))
                             (cdr (gimp-selection-bounds image)))

                    ;; Extract and save the contents as a new file
                    (gimp-edit-copy drawable)
                    (let* ((image    (car (gimp-edit-paste-as-new)))
                           (drawable (car (gimp-image-get-active-layer image)))
                           (filename ""))
                      (while (or (equal? "" filename) (file-exists? filename))
                        (let* ((digits (number->string number))
                               (zeros (substring "0000" (string-length digits))))
                          (set! filename (string-append prefix zeros digits suffix)))
                        (set! number (+ number 1)))
                      (gimp-file-save RUN-NONINTERACTIVE image drawable filename filename)
                      (gimp-image-delete image)))
                  (vector->list (cadr (gimp-vectors-get-strokes vectors))))
        (gimp-image-remove-vectors image vectors))))

  ;; End
  (gimp-selection-none image)
  (gimp-image-undo-group-end image))

(script-fu-register "script-fu-export-selected-regions"
                    "Export Selected Regions"
                    "Export each selected region to a separate file."
                    "Andrew Kvalheim <[email protected]>"
                    "Andrew Kvalheim <[email protected]>"
                    "2012"
                    "RGB* GRAY* INDEXED*"
                    SF-IMAGE "Image" 0
                    SF-DRAWABLE "Drawable" 0)
(script-fu-menu-register "script-fu-export-selected-regions" "<Image>/Select")

Большое спасибо Эндрю Квалхейму за его скрипт-фу.