Я использую Raspberry Pi Zero, скрипт Python 3 и OpenCV.
Я пытаюсь создать однопиксельный непрерывный путь, который очерчивает «твердую» поверхность от «мягкой» поверхности на изображениях, как это:
Изображение, которое я хочу получить в итоге, это «КРАСНАЯ линия (на изображении ниже в формате JPG) на сплошном белом фоне» (наложена на оригинал, чтобы показать его местоположение). Я надеюсь, что смогу получить относительно плавный результат в пределах 10 пикселей от моего желаемого пути:
До сих пор я пришел к выводу, что должен иметь возможность использовать cv.watershed в качестве основной функции для достижения этой цели, но результаты пока не так хороши.
Чтобы упростить мою кривую обучения OpenCV, я изменил пример watershed.py.
Перед тем, как использовать водораздел, я делаю Gausian Blurr с радиусом 150 пикселей, который пока дает мне лучшие результаты.
Затем я размещаю два маркера водораздела, по одному на каждой стороне изображения, и пытаюсь, чтобы водораздел определял желаемую границу.
Вот скрипт Python, который использует водораздел (извините, что использовал неправильный фрагмент, но вставка примера кода не сработала):
#!/usr/bin/env python
'''
Watershed segmentation
=========
This program demonstrates the watershed segmentation algorithm
in OpenCV: watershed().
Usage
-----
watershed.py [image filename]
Keys
----
1-7 - switch marker color
SPACE - update segmentation
r - reset
a - toggle autoupdate
ESC - exit
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
class Sketcher:
def __init__(self, windowname, dests, colors_func):
self.prev_pt = None
self.windowname = windowname
self.dests = dests
self.colors_func = colors_func
self.dirty = False
self.show()
cv.setMouseCallback(self.windowname, self.on_mouse)
def show(self):
cv.imshow(self.windowname, self.dests[0])
def on_mouse(self, event, x, y, flags, param):
pt = (x, y)
if event == cv.EVENT_LBUTTONDOWN:
self.prev_pt = pt
elif event == cv.EVENT_LBUTTONUP:
self.prev_pt = None
if self.prev_pt and flags & cv.EVENT_FLAG_LBUTTON:
for dst, color in zip(self.dests, self.colors_func()):
cv.line(dst, self.prev_pt, pt, color, 5)
self.dirty = True
self.prev_pt = pt
self.show()
class App:
def __init__(self, fn):
self.img = cv.imread(fn)
if self.img is None:
raise Exception('Failed to load image file: %s' % fn)
h, w = self.img.shape[:2]
self.markers = np.zeros((h, w), np.int32)
self.markers_vis = self.img.copy()
self.cur_marker = 1
self.colors = np.int32( list(np.ndindex(2, 2, 2)) ) * 255
self.auto_update = True
self.sketch = Sketcher('img', [self.markers_vis, self.markers], self.get_colors)
def get_colors(self):
return list(map(int, self.colors[self.cur_marker])), self.cur_marker
def watershed(self):
m = self.markers.copy()
cv.watershed(self.img, m)
overlay = self.colors[np.maximum(m, 0)]
oim = cv.imread('1.JPG')
vis = cv.addWeighted(oim, 0.5, overlay, 0.5, 0.0, dtype=cv.CV_8UC3)
cv.imshow('watershed', vis)
def run(self):
while cv.getWindowProperty('img', 0) != -1 or cv.getWindowProperty('watershed', 0) != -1:
ch = cv.waitKey(50)
if ch == 27:
break
if ch >= ord('1') and ch <= ord('7'):
self.cur_marker = ch - ord('0')
print('marker: ', self.cur_marker)
if ch == ord(' ') or (self.sketch.dirty and self.auto_update):
self.watershed()
self.sketch.dirty = False
if ch in [ord('a'), ord('A')]:
self.auto_update = not self.auto_update
print('auto_update if', ['off', 'on'][self.auto_update])
if ch in [ord('r'), ord('R')]:
self.markers[:] = 0
self.markers_vis[:] = self.img
self.sketch.show()
cv.destroyAllWindows()
if __name__ == '__main__':
import sys
try:
fn = sys.argv[1]
except:
fn = '1-gb150.JPG'
print(__doc__)
App(fn).run()
Вот лучший водораздел, который я когда-либо видел:
Какие еще варианты водораздела / процесса я могу использовать, или, если водораздел не является лучшим подходом для этой проблемы, какой другой подход можно добавить / использовать вместо этого?
(Изменить: На время выполнения мне понадобится 1-2 секунды на Raspberry Pi Zero)
(Ред. 3/7/2018 # 1: Условия, которые могут помочь в дальнейшем определении правил алгоритма для упрощения процесса.
- Мой желаемый путь никогда не был бы островами.
- Мой желаемый путь может быть уменьшен до линий, которые входят в один край изображения и покидают другой край изображения, выбирая при этом «самый прямой» путь. Последовательность изображений всегда может начинаться в этом состоянии.
- Возможно, если будет достигнут какой-либо угол, алгоритм может остановиться и попросить помощи. (т. е. для обработки изображения, имеющего угол> 90 градусов).
- Ряд изображений всегда будет иметь некоторую точку на желаемой линии, которая была определена из предыдущего изображения, которая могла бы использоваться в качестве начального пикселя для алгоритма 'отслеживания кромок' в новом изображении (отслеживание кромок становится затруднительным, когда трава растет через край, но я мог бы сначала съесть траву).
(Edit: более твердые / мягкие изображения поверхности, на которых мне нужно использовать алгоритм):
(конец больше)