Порог с помощью OpenCv? - PullRequest
       62

Порог с помощью OpenCv?

0 голосов
/ 10 января 2020

Как показывают вопросы, я хочу применить двухстороннюю технику адаптивного порога к своему изображению. То есть я хочу найти значение каждого пикселя в окрестности и установить его равным 255, если оно меньше или больше среднего значения окрестности минус константа c.

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

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

enter image description here

Ответы [ 2 ]

2 голосов
/ 10 января 2020

Ваш вопрос и комментарий противоречивы: держите все (значительно) ярче / темнее, чем среднее значение (+/- константа) соседства (вопрос), и держите все в пределах среднего +/- константа (комментарий). Я предполагаю, что первый из них правильный, и я постараюсь дать ответ.

Использование cv2.adaptiveThreshold, безусловно, полезно; Параметризация может быть сложной, особенно учитывая пример изображения. Сначала давайте посмотрим на результат:

Output

Мы видим, что диапазон значений интенсивности на данном изображении мал. Верхние половины третьего и шестого квадратов на самом деле не отличаются от их окрестностей. Там вряд ли можно найти правильную разницу. Более вероятно, что верхние половины квадратов № 8 и № 12 (или также нижняя половина квадрата № 10) будут найдены.

В верхней строке теперь отображаются некоторые «глобальные» параметры (blocksize = 151 , c = 25), в нижнем ряду больше «локальных» параметров (blocksize = 51, c = 5). В среднем столбце все темнее, чем в окрестности (относительно параметров), в правом столбце все ярче, чем в окрестности. Мы видим, что в более «глобальном» случае мы получаем правильные верхние половины, но в большинстве случаев нет «значительных» более темных областей. Глядя на более «локальный» случай, мы видим более темные области, но мы не найдем полных верхних / нижних половин, о которых идет речь. Это объясняется тем, как устроены различные треугольники.

С технической стороны: вам нужно два вызова cv2.adaptiveThreshold, один из которых использует режим cv2.THRESH_BINARY_INV, чтобы найти все темнее, а другой - режим cv2.THRESH_BINARY, чтобы найди все ярче Кроме того, вы должны предоставить c или -c для двух разных случаев.

Вот полный код:

import cv2
from matplotlib import pyplot as plt
from skimage import io          # Only needed for web grabbing images

plt.figure(1, figsize=(15, 10))

img = cv2.cvtColor(io.imread('https://i.stack.imgur.com/dA1Vt.png'), cv2.COLOR_RGB2GRAY)

plt.subplot(2, 3, 1), plt.imshow(img, cmap='gray'), plt.colorbar()

# More "global" parameters
bs = 151
c = 25
img_le = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, c)
img_gt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, bs, -c)
plt.subplot(2, 3, 2), plt.imshow(img_le, cmap='gray')
plt.subplot(2, 3, 3), plt.imshow(img_gt, cmap='gray')

# More "local" parameters
bs = 51
c = 5
img_le = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, c)
img_gt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, bs, -c)
plt.subplot(2, 3, 5), plt.imshow(img_le, cmap='gray')
plt.subplot(2, 3, 6), plt.imshow(img_gt, cmap='gray')

plt.tight_layout()
plt.show()

Надеюсь, что поможет - как-то!

-----------------------
System information
-----------------------
Python:      3.8.1
Matplotlib:  3.2.0rc1
OpenCV:      4.1.2
-----------------------
1 голос
/ 11 января 2020

Еще один способ взглянуть на это - то, что где abs (среднее - изображение) <= c, вы хотите, чтобы он стал белым, иначе вы хотите, чтобы он стал черным. В Python / OpenCV / Scipy / Numpy я сначала вычисляю локальное равномерное среднее значение (среднее), используя равномерный фильтр усреднения блоков 51x51 пикселей (среднее значение по вагонам). Если хотите, вы можете использовать метод взвешенного усреднения, например, среднее гауссовское. Затем я вычисляю абс (среднее - изображение). Тогда я использую Numpy порог. Примечание: Вы также можете просто использовать один простой порог (cv2.threshold) для результата abs (среднее изображение) вместо двух numpy порогов. </p>

Ввод:

enter image description here

import cv2
import numpy as np
from scipy import ndimage

# read image as grayscale
# convert to floats in the range 0 to 1 so that the difference keeps negative values
img = cv2.imread('squares.png',0).astype(np.float32)/255.0

# get uniform (51x51 block) average
ave = ndimage.uniform_filter(img, size=51)

# get abs difference between ave and img and convert back to integers in the range 0 to 255
diff = 255*np.abs(ave - img)
diff = diff.astype(np.uint8)

# threshold
# Note: could also just use one simple cv2.Threshold on diff
c = 5
diff_thresh = diff.copy()
diff_thresh[ diff_thresh <= c ] = 255
diff_thresh[ diff_thresh != 255 ] = 0


# view result
cv2.imshow("img", img)
cv2.imshow("ave", ave)
cv2.imshow("diff", diff)
cv2.imshow("threshold", diff_thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save result
cv2.imwrite("squares_2way_thresh.jpg", diff_thresh)


Результат:

enter image description here

...