У меня большое двоичное изображение (4k x 7k pix), из которого я хочу извлечь всю желтую часть в виде одного прямоугольника. Я попробовал бинарную эрозию, чтобы выровнять объекты внутри желтой области. Затем я использовал метод bbox
для skimage.regionprops
но он не работает достаточно быстро для большого изображения с одним большим bbox. Есть ли у вас предложения?
Поскольку изображение, которое вы предоставили, включает в себя отвлекающие оси, и является неправильным цветом и слишком маленьким, я создал как реалистичную версию, как я мог с ImageMagick, как это в терминале:
convert bbox.png -alpha off -crop 120x215+40+13 -colorspace gray -normalize -threshold 50% -scale 4200x7200\! bbox.png
Полноразмерная версия - 4200x7200.
Затем я написал numpy
версию -based на bbox
следующим
#!/usr/local/bin/python3
import numpy as np
from PIL import Image
def bbox(image):
"""Find bounding box of image"""
# Project all columns into row same width as image
proj=np.any(image,axis=0)
# Find first non-zero value from Left
L=np.argmax(proj)
# And right
R=image.shape[1]-np.argmax(np.flipud(proj))-1
# Project all rows into column same height as image
proj=np.any(image,axis=1)
# Find first non-zero value from Top
T=np.argmax(proj)
# And Bottom
B=image.shape[0]-np.argmax(np.flipud(proj))-1
return T,L,B,R
image=np.array(Image.open("a.png").convert("L"))
print(bbox(image))
Это работает на 5,3 мс на моем Mac. Просто для удовольствия я нарезал резьбу и выполнил горизонтальную проекцию и вертикальную проекцию на отдельные параллельные потоки, и она достигла 3,6 мс с теми же результатами.
#!/usr/local/bin/python3
import numpy as np
from PIL import Image
import threading
import queue
def DoOneDim(image,axis,q):
"""Find bounding box of image"""
proj=np.any(image,axis=axis)
# Find first non-zero value
A=np.argmax(proj)
# And and last
B=image.shape[1-axis]-np.argmax(np.flipud(proj))-1
q.put({axis:(A,B)})
def bboxTh(image):
"""Threaded version of bbox() that does vertical and horizontal on their own theads"""
q = queue.Queue()
Hthread=threading.Thread(target=DoOneDim, args=(image,0,q))
Vthread=threading.Thread(target=DoOneDim, args=(image,1,q))
Hthread.start()
Vthread.start()
Hthread.join()
Vthread.join()
results=dict()
while not q.empty():
results.update(q.get())
return results
image=np.array(Image.open("a.png").convert("L"))
print(bboxTh(image))
Выбранное поле выглядит так:
Поскольку вы ищете одну ограничительную рамку, не используйте regionprops
или любую функцию для каждого объекта. Это также делает так, что вам не нужно пытаться сделать один объект из всех желтых точек.
Простейшим решением здесь является прогулка по изображению, и для каждого пикселя определить, достаточно ли он "достаточно желтый" (что бы это ни значило для вашего приложения). Если это так, добавьте пиксельные координаты в расчет выполняемой рамки.
Расчет ограничивающей коробки довольно прост:
top_left = [1e9, 1e9]
bottom_right = [0, 0]
for ...:
# within your loop over the pixels, [x, y] are the current coordinates
top_left = [min(top_left[0], x), min(top_left[1], y)];
bottom_right = [max(bottom_right[0], x), max(bottom_right[1], y)];
Возможно, с помощью skimage можно сделать это без циклов, но я не знаю этого пакета вообще.