Как применить водораздел к изображению в градациях серого с помощью opencv и python?

На основе решения, которое я прочитал в Как определить маркеры для Водораздел в OpenCV?, я пытаюсь применить водораздел к данным в градациях серого (не очень видимым, но не полностью черным), полученный из netcdf (данные об осадках).

Вот черно-белая версия данных (порог 0), чтобы вам было легче видеть, и маркеры, которые я хочу использовать для определения различных бассейнов (по сути, просто еще один порог, при котором осадки более интенсивны).

Код, который я запускаю, выглядит следующим образом:

import os,sys,string
from netCDF4 import Dataset as nc
import cv2
import numpy as np
import matplotlib.pyplot as mpl
import scipy.ndimage as ndimage
import scipy.spatial as spatial
from skimage import filter
from skimage.morphology import watershed
from scipy import ndimage

filename=["Cmorph-1999_01_03.nc"]

nc_data=nc(filename[0])
data=nc_data.variables["CMORPH"][23,0:250,250:750]
new_data=np.flipud(data)
ma_data=np.ma.masked_where(new_data<=0,new_data)
ma_conv=np.ma.masked_where(new_data<=2,new_data)

## Borders
tmp_data=ma_data.filled(0)
tmp_data[np.where(tmp_data!=0)]=255
bw_data=tmp_data.astype(np.uint8)
border = cv2.dilate(bw_data, None, iterations=5)
border = border - cv2.erode(border, None)

## Markers
tmp_conv=ma_conv.filled(0)
tmp_conv[np.where(tmp_conv!=0)]=255
bw_conv=tmp_conv.astype(np.uint8)
lbl, ncc = ndimage.label(bw_conv)
lbl = lbl * (255/ncc)
lbl[border == 255] = 255
lbl = lbl.astype(np.int32)

## Apply watershed
cv2.watershed(ma_data, lbl)

lbl[lbl == -1] = 0
lbl = lbl.astype(np.uint8)
result = 255 - lbl

У меня есть следующая ошибка для водораздела в opencv-2.4.11/modules/imgproc/src/segmentation.cpp:

error: (-210) Only 8-bit, 3-channel input images are supported in function cvWatershed

То, что я видел в Интернете, связано с тем, что данные в градациях серого представляют собой 2D-изображение, а для водораздела требуется 3D-изображение (из RGB). Действительно, я попробовал скрипт с изображением в формате jpg, и он работал отлично. Эта проблема упоминается здесь, но дан ответ, наконец, отклоненный. И я не могу найти более свежую ссылку, отвечающую на вопрос.

Чтобы попытаться решить эту проблему, я создал 3D-массив из 2D-данных new_data:

new_data = new_data[..., np.newaxis]
test=np.append(new_data, new_data, axis=2)
test=np.append(new_data, test, axis=2)

Но, как и ожидалось, это не решило проблему (то же самое сообщение об ошибке).

Я также попытался сохранить график из matplotlib, чтобы получить данные RGB:

fig = mpl.figure()
fig.add_subplot(111)
fig.tight_layout(pad=0)
mpl.contourf(ma_data,levels=np.arange(0,255.1,0.1))
fig.canvas.draw()
test_data = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
test_data = test_data.reshape(fig.canvas.get_width_height()[::-1] + (3,))

Но размер созданных test_data отличается от ma_data (+ не могу избавиться от меток).

Итак, я застрял здесь. В идеале я хочу применить водораздел напрямую к полутоновому 2D-изображению и/или максимально ограничить количество операций.


person dcoppin    schedule 14.11.2017    source источник
comment
Посмотрите   -  person lucians    schedule 14.11.2017


Ответы (2)


Как упомянул yapws87, действительно была проблема с форматом, который я представил функции водораздела. Выполнение try_data=ma_data.astype(np.uint8) удалило сообщение об ошибке.

Вот минимальный пример, который работает сейчас:

import os,sys
from netCDF4 import Dataset as nc
import cv2
import numpy as np
import scipy.ndimage as ndimage
from skimage.morphology import watershed
from scipy import ndimage

basename="/home/dcop696/Data/CMORPH/precip/CMORPH_V1.0/CRT/8km-30min/1999/"
filename=["Cmorph-1999_01_03.nc"]
fileslm=["/home/dcop696/Data/LSM/Cmorph_slm_8km.nc"]

nc_data=nc(basename+filename[0])
data=nc_data.variables["CMORPH"][23,0:250,250:750]
new_data=np.flipud(data)
ma_data=np.ma.masked_where(new_data<=0,new_data)
try_data=ma_data.astype(np.uint8)  

## Building threshold
tmp_data=ma_data.filled(0)
tmp_data[np.where(tmp_data!=0)]=255
bw_data=tmp_data.astype(np.uint8)

## Building markers
ma_conv=np.ma.masked_where(new_data<=2,new_data)
tmp_conv=ma_conv.filled(0)
tmp_conv[np.where(tmp_conv!=0)]=255
bw_conv=tmp_conv.astype(np.uint8)
markers = ndimage.label(bw_conv)[0]

## Watershed
labels = watershed(-try_data, markers, mask=bw_data)
person dcoppin    schedule 15.11.2017

вы можете попробовать изменить серый кадр изображения на цветовое пространство BGR, используя

cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)

перед передачей изображения в алгоритм водораздела

person yapws87    schedule 14.11.2017
comment
Спасибо за предложение, но это дает тот же эффект, что и создание 3D-массива из 2D в моей попытке (тестовая переменная создана), возможно, потому, что у вас недостаточно информации, чтобы вернуться к RGB. - person dcoppin; 14.11.2017
comment
Вам не нужна дополнительная информация, чтобы перейти к RGB, она просто продублирует значения для каждого из каналов RGB. Я предполагаю, что формат, который вы представляете функции водораздела, неверен. - person yapws87; 14.11.2017
comment
Ваше предположение было правильным. Первый аргумент в водоразделе имеет неправильный формат. Ошибка не имела ничего общего с размером изображения в градациях серого. - person dcoppin; 15.11.2017