Подтвердить что ты не робот

Обнаружение края для изображения, хранящегося в матрице

Я представляю изображения в виде двухмерных массивов. У меня есть эта картина:

original

Как я могу получить пиксели, которые находятся непосредственно на границах серой области и раскрасить их?

colorized

Я хочу, чтобы координаты элементов матрицы были зелеными и красными отдельно. У меня есть только белые, черные и серые области на матрице.

4b9b3361

Ответ 1

Мы надеемся, что все будет хорошо для ваших нужд (или, по крайней мере, помочь). Идея состоит в разделении на различные регионы с использованием логических проверок на основе пороговых значений. Затем грань между этими областями может быть обнаружена с помощью numpy roll для сдвига пикселей по x и y и сравнения, если мы находимся на краю,

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
from skimage.morphology import closing

thresh1 = 127
thresh2 = 254

#Load image
im = sp.misc.imread('jBD9j.png')

#Get threashold mask for different regions
gryim = np.mean(im[:,:,0:2],2)
region1 =  (thresh1<gryim)
region2 =  (thresh2<gryim)
nregion1 = ~ region1
nregion2 = ~ region2

#Plot figure and two regions
fig, axs = plt.subplots(2,2)
axs[0,0].imshow(im)
axs[0,1].imshow(region1)
axs[1,0].imshow(region2)

#Clean up any holes, etc (not needed for simple figures here)
#region1 = sp.ndimage.morphology.binary_closing(region1)
#region1 = sp.ndimage.morphology.binary_fill_holes(region1)
#region1.astype('bool')
#region2 = sp.ndimage.morphology.binary_closing(region2)
#region2 = sp.ndimage.morphology.binary_fill_holes(region2)
#region2.astype('bool')

#Get location of edge by comparing array to it 
#inverse shifted by a few pixels
shift = -2
edgex1 = (region1 ^ np.roll(nregion1,shift=shift,axis=0))
edgey1 = (region1 ^ np.roll(nregion1,shift=shift,axis=1))
edgex2 = (region2 ^ np.roll(nregion2,shift=shift,axis=0)) 
edgey2 = (region2 ^ np.roll(nregion2,shift=shift,axis=1))

#Plot location of edge over image
axs[1,1].imshow(im)
axs[1,1].contour(edgex1,2,colors='r',lw=2.)
axs[1,1].contour(edgey1,2,colors='r',lw=2.)
axs[1,1].contour(edgex2,2,colors='g',lw=2.)
axs[1,1].contour(edgey2,2,colors='g',lw=2.)
plt.show()

Что дает following. Для простоты я использую roll с инверсией каждого региона. Вы можете перевернуть каждую последующую область на следующую, чтобы обнаружить края

Спасибо @Kabyle за вознаграждение, это проблема, которую я потратил некоторое время на поиск решения. Я пробовал scipy skeletonize, feature.canny, модуль топологии и openCV с ограниченным успехом... Этот способ был самым надежным для моего случая (отслеживание интерфейса капель). Надеюсь, это поможет!

Ответ 2

Существует очень простое решение: по определению любой пиксель, у которого есть белый и серый сосед, находится на вашем "красном" краю, а серые и черные соседи находятся на "зеленом" краю. Самые светлые/самые темные соседи возвращаются максимальными/минимальными фильтрами в skimage.filters.rank, а двоичная комбинация масок пикселей с самым светлым/самым темным соседом белого/серого или серого/черного соответственно создает ребра.

Результат:

enter image description here

Отработанное решение:

import numpy
import skimage.filters.rank
import skimage.morphology
import skimage.io

# convert image to a uint8 image which only has 0, 128 and 255 values
# the source png image provided has other levels in it so it needs to be thresholded - adjust the thresholding method for your data
img_raw = skimage.io.imread('jBD9j.png', as_grey=True)
img = numpy.zeros_like(img, dtype=numpy.uint8)
img[:,:] = 128
img[ img_raw < 0.25 ] = 0
img[ img_raw > 0.75 ] = 255

# define "next to" - this may be a square, diamond, etc
selem = skimage.morphology.disk(1)

# create masks for the two kinds of edges
black_gray_edges = (skimage.filters.rank.minimum(img, selem) == 0) & (skimage.filters.rank.maximum(img, selem) == 128)
gray_white_edges = (skimage.filters.rank.minimum(img, selem) == 128) & (skimage.filters.rank.maximum(img, selem) == 255)

# create a color image
img_result = numpy.dstack( [img,img,img] )

# assign colors to edge masks
img_result[ black_gray_edges, : ] = numpy.asarray( [ 0, 255, 0 ] )
img_result[ gray_white_edges, : ] = numpy.asarray( [ 255, 0, 0 ] )

imshow(img_result)

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

P.S. Края имеют ширину в два пикселя. Об этом не обойтись без дополнительной информации: края находятся между двумя областями, и вы не определили, какую из двух областей вы хотите, чтобы они перекрывались в каждом случае, поэтому единственное симметричное решение состоит в том, чтобы перекрывать обе области одним пикселей.

P.S. Это считает сам пиксель своим соседом. Изолированный белый или черный пиксель на сером или наоборот будет рассматриваться как край (а также все пиксели вокруг него).

Ответ 3

В то время как ответ plonser может быть довольно прямым для реализации, я вижу, что он не работает, когда речь заходит о резких и тонких краях. Тем не менее, я предлагаю вам использовать часть своего подхода в качестве предварительной подготовки.
На втором этапе вы хотите использовать алгоритм Marching Squares. Согласно документации scikit-image, это

частный случай алгоритма маршевых кубов (Лоренсен, Уильям и Харви Э. Клайн. Маршевые кубики: 3D-поверхность высокого разрешения Алгоритм построения. Компьютерная графика (SIGGRAPH 87 Proceedings) 21 (4) июль 1987 года, стр. 163-170

Существует даже реализация Python как часть пакета scikit-image. Я использовал этот алгоритм (моя собственная реализация Fortran, хотя) успешно для обнаружения границ глазных диаграмм в коммуникационной технике.

Объявление 1: Предварительная подготовка
Создайте копию своего изображения и сделайте только два цвета, например. черно-белый. Координаты остаются неизменными, но вы убедитесь, что алгоритм может правильно сделать да/нет-решение независимым от значений, которые вы используете в матричном представлении изображения.

Объявление 2: Обнаружение края
Wikipedia, а также различные блоги предоставляют вам довольно подробное описание алгоритма на разных языках, поэтому я не буду вдаваться в него Детали. Однако позвольте мне дать вам несколько практических советов:

  • У вашего изображения есть открытые границы внизу. Вместо изменения алгоритма вы можете искусственно добавить еще одну строку пикселей (черный или серый, чтобы связать белые/серые области).
  • Выбор начальной точки имеет решающее значение. Если обрабатывать не так много изображений, я предлагаю вам выбрать его вручную. В противном случае вам нужно будет определить правила. Поскольку алгоритм Marching Squares может начинаться где угодно внутри ограниченной области, вы можете выбрать любой пиксель заданного цвета/значения для обнаружения соответствующего края (он сначала начнет идти в одном направлении, чтобы найти край).
  • Алгоритм возвращает точные 2D-позиции, например. (Х/у) -наборов. Вы также можете

Наконец: некоторая пост-обработка Я предложил добавить искусственную границу к изображению. Это имеет два преимущества:  1. Алгоритм маршевых квадратов работает из коробки.  2. Нет необходимости различать границу изображения и интерфейс между двумя областями внутри изображения. Просто удалите искусственную границу, как только вы закончите установку цветных краев - это приведет к удалению цветных линий на границе изображения.

Ответ 4

В основном, следуя предложению pyStarter об использовании алгоритма маршевого квадрата из scikit-изображения, желаемые контуры могут быть извлечены с помощью следующего кода:

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
from skimage import measure
import scipy.ndimage as ndimage
from skimage.color import rgb2gray
from pprint import pprint
#Load image
im = rgb2gray(sp.misc.imread('jBD9j.png'))

n, bins_edges = np.histogram(im.flatten(),bins = 100)
# Skip the black area, and assume two distinct regions, white and grey
max_counts = np.sort(n[bins_edges[0:-1] > 0])[-2:]
thresholds = np.select(
    [max_counts[i] == n for i in range(max_counts.shape[0])],
    [bins_edges[0:-1]] * max_counts.shape[0]
)
# filter our the non zero values
thresholds = thresholds[thresholds > 0] 


fig, axs = plt.subplots()
# Display image
axs.imshow(im, interpolation='nearest', cmap=plt.cm.gray)
colors = ['r','g']
for i, threshold in enumerate(thresholds):
    contours = measure.find_contours(im, threshold)

    # Display  all contours found for this threshold
    for n, contour in enumerate(contours):
        axs.plot(contour[:,1], contour[:,0],colors[i], lw = 4)

axs.axis('image')
axs.set_xticks([])
axs.set_yticks([])        
plt.show()

Edges!

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

Ответ 5

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

import numpy as np

# assuming black -> 0 and white -> 1 and grey -> 0.5
black_reg = np.where(a < 0.1, a, 10)
white_reg = np.where(a > 0.9, a, 10)

xx_black,yy_black = np.gradient(black_reg)
xx_white,yy_white = np.gradient(white_reg)

# getting the coordinates
coord_green = np.argwhere(xx_black**2 + yy_black**2>0.2)
coord_red   = np.argwhere(xx_white**2 + yy_white**2>0.2)

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

Ответ 6

Я думаю, что вы, вероятно, ищете метод обнаружения границ для изображений с серой шкалой. Есть много способов сделать это. Возможно, это поможет http://en.m.wikipedia.org/wiki/Edge_detection. Для дифференциации краев между белым и серым и краями между черным и серым используйте среднюю локальную интенсивность.