53

मैं ओपनसीवी के साथ एंड्रॉइड के लिए लिख रहा हूं। मैं मैन्युअल रूप से छवि को चिह्नित किए बिना, मार्कर-नियंत्रित वाटरशेड का उपयोग करके नीचे की तरह एक छवि को विभाजित कर रहा हूं। मैं क्षेत्रीय maxima मार्कर के रूप में उपयोग करने की योजना बना रहा हूँ।ओपनसीवी में वॉटरशेड के लिए मार्कर को कैसे परिभाषित किया जाए?

minMaxLoc() मुझे मूल्य देगा, लेकिन मैं इसे किस ब्लॉब्स में प्रतिबंधित कर सकता हूं, जिसमें मुझे रूचि है? क्या मैं आरओआई को प्रतिबंधित करने के लिए findContours() या सीवीब्लॉब ब्लॉब्स से परिणामों का उपयोग कर सकता हूं और प्रत्येक ब्लॉब में अधिकतमता लागू कर सकता हूं?

input image

+1

@mmgp द्वारा जवाब इस सवाल का सही समाधान है। मुझे लगता है कि आपको इसे स्वीकार्य उत्तर के रूप में चिह्नित करना चाहिए। –

उत्तर

91

सबसे पहले: समारोह minMaxLoc केवल वैश्विक न्यूनतम और किसी दिए गए इनपुट के लिए वैश्विक अधिकतम पाता है, तो यह ज्यादातर क्षेत्रीय न्यूनतम और/या क्षेत्रीय मॅक्सिमा निर्धारण करने के लिए बेकार है। लेकिन आपका विचार सही है, मार्करों के आधार पर वाटरशेड ट्रांसफॉर्म करने के लिए क्षेत्रीय मिनीमा/मैक्सिमा के आधार पर मार्कर निकालने के लिए बिल्कुल ठीक है। मुझे वॉटरशेड ट्रांसफॉर्म क्या है और ओपनसीवी में मौजूद कार्यान्वयन का सही तरीके से उपयोग करने के तरीके को स्पष्ट करने का प्रयास करें।

वाटरशेड से निपटने वाले कुछ अच्छे कागजात इस तरह के वर्णन के समान वर्णन करते हैं (यदि आप अनिश्चित हैं, तो मुझे कुछ विवरण याद आ सकते हैं: पूछें)। आप जानते हैं कि कुछ क्षेत्र की सतह पर विचार करें, इसमें घाटियों और चोटियों (अन्य विवरणों के बीच जो हमारे लिए अप्रासंगिक हैं) शामिल हैं। मान लीजिए कि इस सतह के नीचे आपके पास पानी, रंगीन पानी है। अब, अपनी सतह की प्रत्येक घाटी में छेद बनाओ और फिर पानी सभी क्षेत्र को भरना शुरू कर देता है। कुछ बिंदु पर, अलग-अलग रंगीन पानी मिलेंगे, और जब ऐसा होता है, तो आप एक बांध बनाते हैं जैसे कि वे एक दूसरे को छूते नहीं हैं। अंत में आपके पास बांधों का संग्रह होता है, जो वाटरशेड सभी अलग-अलग रंगीन पानी को अलग करता है।

अब, यदि आप उस सतह में बहुत अधिक छेद बनाते हैं, तो आप बहुत से क्षेत्रों के साथ समाप्त होते हैं: अधिक सेगमेंटेशन।यदि आप बहुत कम करते हैं तो आपको एक सेगमेंटेशन मिलता है। तो, वस्तुतः कोई भी पेपर जो वाटरशेड का उपयोग करने का सुझाव देता है वास्तव में पेपर से निपटने वाले आवेदन के लिए इन समस्याओं से बचने के लिए तकनीकों को प्रस्तुत करता है।

मैंने यह सब लिखा है (जो किसी भी व्यक्ति के लिए संभवतः बहुत भद्दा है जो जानता है कि वाटरशेड ट्रांसफॉर्म क्या है) क्योंकि यह सीधे वाटरशेड कार्यान्वयन का उपयोग करना चाहिए (जो वर्तमान स्वीकृत उत्तर पूरी तरह से गलत तरीके से कर रहा है)। आइए पाइथन बाइंडिंग का उपयोग करके, ओपनसीवी उदाहरण पर शुरू करें।

प्रश्न में प्रस्तुत छवि कई वस्तुओं से बना है जो अधिकतर करीब हैं और कुछ मामलों में अतिव्यापी हैं। यहां वाटरशेड की उपयोगिता इन वस्तुओं को सही ढंग से अलग करना है, उन्हें एक घटक में समूहित नहीं करना है। तो आपको प्रत्येक ऑब्जेक्ट के लिए कम से कम एक मार्कर और पृष्ठभूमि के लिए अच्छे मार्करों की आवश्यकता है। उदाहरण के तौर पर, पहले ओत्सु द्वारा इनपुट छवि को बिनरिज करें और छोटी वस्तुओं को हटाने के लिए एक मोर्फोलॉजिकल ओपनिंग करें। इस चरण का परिणाम बाएं छवि में नीचे दिखाया गया है। अब बाइनरी छवि के साथ दूरी को बदलने में विचार करने पर विचार करें, परिणाम सही है।

enter image description hereenter image description here

दूरी को बदलने परिणाम के साथ

, हम कुछ सीमा ऐसी है कि हम केवल उन्हीं क्षेत्रों पृष्ठभूमि (नीचे बाईं छवि) के लिए सबसे दूर करने पर विचार करने पर विचार कर सकते हैं। ऐसा करने से, हम प्रत्येक ऑब्जेक्ट के लिए पहले थ्रेसहोल्ड के बाद विभिन्न क्षेत्रों को लेबल करके मार्कर प्राप्त कर सकते हैं। अब, हम अपने मार्कर लिखने के लिए उपरोक्त बाएं छवि के एक विस्तृत संस्करण की सीमा पर भी विचार कर सकते हैं। पूरा मार्कर नीचे दिखाया गया है (कुछ मार्कर बहुत अंधेरे हैं, लेकिन बाएं छवि में प्रत्येक सफेद क्षेत्र को सही छवि पर दर्शाया जाता है)।

enter image description hereenter image description here

यह मार्कर हम यहाँ है भावना का एक बहुत बनाता है। प्रत्येक colored water == one marker क्षेत्र को भरना शुरू कर देगा, और वाटरशेड परिवर्तन बांधों का निर्माण करेगा ताकि अलग-अलग "रंग" विलय हो जाएं। अगर हम ट्रांसफॉर्म करते हैं, तो हमें छवि बाईं ओर मिलती है। मूल छवि के साथ उन्हें लिखकर बांधों को ध्यान में रखते हुए, हमें परिणाम सही मिलते हैं।

enter image description hereenter image description here

import sys 
import cv2 
import numpy 
from scipy.ndimage import label 

def segment_on_dt(a, img): 
    border = cv2.dilate(img, None, iterations=5) 
    border = border - cv2.erode(border, None) 

    dt = cv2.distanceTransform(img, 2, 3) 
    dt = ((dt - dt.min())/(dt.max() - dt.min()) * 255).astype(numpy.uint8) 
    _, dt = cv2.threshold(dt, 180, 255, cv2.THRESH_BINARY) 
    lbl, ncc = label(dt) 
    lbl = lbl * (255/(ncc + 1)) 
    # Completing the markers now. 
    lbl[border == 255] = 255 

    lbl = lbl.astype(numpy.int32) 
    cv2.watershed(a, lbl) 

    lbl[lbl == -1] = 0 
    lbl = lbl.astype(numpy.uint8) 
    return 255 - lbl 


img = cv2.imread(sys.argv[1]) 

# Pre-processing. 
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  
_, img_bin = cv2.threshold(img_gray, 0, 255, 
     cv2.THRESH_OTSU) 
img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, 
     numpy.ones((3, 3), dtype=int)) 

result = segment_on_dt(img, img_bin) 
cv2.imwrite(sys.argv[2], result) 

result[result != 255] = 0 
result = cv2.dilate(result, None) 
img[result == 255] = (0, 0, 255) 
cv2.imwrite(sys.argv[3], img) 
+1

ट्यूटोरियल के लिए धन्यवाद। वाटरशेड एल्गोरिदम से अपरिचित हमारे लिए बहुत अच्छी तरह से समझाया गया है। जैसा कि आपने बताया है, सेगमेंट ऑब्जेक्ट्स की संख्या ज्यादातर प्राथमिक चरणों में कितनी मार्कर पाई गई थी और यहां पर निर्भर करता है कि यह दूरी परिवर्तन के बाद थ्रेसहोल्डिंग के बाद कुछ गोलियों को विभाजित किया गया है। क्या हम थ्रेसहोल्डिंग के पैरामीटर को बदलकर परिणामों में सुधार कर सकते हैं? – Denis

+1

यह 'एलबीएल * (255/(एनसीसी + 1) होना चाहिए) अन्यथा एक समोच्च खो गया है – Nikita

45

मैं कैसे यहाँ वाटरशेड का उपयोग करने पर एक सरल कोड की व्याख्या करना चाहते हैं। मैं ओपनसीवी-पायथन का उपयोग कर रहा हूं, लेकिन मुझे आशा है कि आपको समझने में कोई कठिनाई नहीं होगी।

इस कोड में, मैं अग्रभूमि-पृष्ठभूमि निष्कर्षण के लिए एक उपकरण के रूप में वाटरशेड का उपयोग करूँगा। (यह उदाहरण ओपनसीवी कुकबुक में सी ++ कोड का पायथन समकक्ष है)। वाटरशेड को समझने के लिए यह एक साधारण मामला है। इसके अलावा, आप इस छवि में वस्तुओं की संख्या की गणना करने के लिए वाटरशेड का उपयोग कर सकते हैं। यह इस कोड का थोड़ा उन्नत संस्करण होगा।

- सबसे पहले हम अपनी छवि लोड करते हैं, इसे ग्रेस्केल में परिवर्तित करते हैं, और इसे उपयुक्त मान के साथ थ्रेसहोल्ड करते हैं। मैंने ओत्सु के बिनराइजेशन लिया, इसलिए यह सबसे अच्छा थ्रेसहोल्ड मान मिलेगा।

import cv2 
import numpy as np 

img = cv2.imread('sofwatershed.jpg') 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 

नीचे परिणाम मुझे मिल गया है:

enter image description here

(यहां तक ​​कि परिणाम अच्छा है, क्योंकि अग्रभूमि और पृष्ठभूमि छवियों के बीच महान विपरीत)

2 - अब हम बनाने के लिए चिह्नक। मार्कर मूल छवि के समान आकार वाली छवि है जो 32SC1 (32 बिट हस्ताक्षरित एकल चैनल) है।

अब मूल छवि में कुछ क्षेत्र होंगे जहां आप बस निश्चित हैं, वह हिस्सा अग्रभूमि से संबंधित है। मार्कर छवि में 255 के साथ इस क्षेत्र को चिह्नित करें। अब वह क्षेत्र जहां आप पृष्ठभूमि होने के लिए निश्चित हैं 128 के साथ चिह्नित हैं। जिस क्षेत्र को आप सुनिश्चित नहीं कर रहे हैं उसे 0 के साथ चिह्नित किया गया है। यही वह है कि हम आगे बढ़ेंगे।

ए - अग्रभूमि क्षेत्र: - हमें पहले से ही एक थ्रेसहोल्ड छवि मिल गई है जहां गोलियाँ सफेद रंग हैं। हमने उन्हें थोड़ा खराब कर दिया, ताकि हम सुनिश्चित कर सकें कि शेष क्षेत्र अग्रभूमि से संबंधित है।

fg = cv2.erode(thresh,None,iterations = 2) 

FG:

enter image description here

बी - पृष्ठभूमि क्षेत्र: - यहाँ हम thresholded छवि फैलने ताकि पृष्ठभूमि क्षेत्र कम हो जाता है। लेकिन हमें यकीन है कि शेष काला क्षेत्र 100% पृष्ठभूमि है। हम इसे 128 पर सेट करते हैं।इस प्रकार

bgt = cv2.dilate(thresh,None,iterations = 3) 
ret,bg = cv2.threshold(bgt,1,128,1) 

अब हम bg मिलती है: -

enter image description here

सी अब हम दोनों FG और bg जोड़ें:

marker = cv2.add(fg,bg) 

नीचे हम क्या मिलता है:

enter image description here

अब हम उपरोक्त छवि से स्पष्ट रूप से समझ सकते हैं, कि सफेद क्षेत्र 100% अग्रभूमि है, ग्रे क्षेत्र 100% पृष्ठभूमि है, और काला क्षेत्र हम निश्चित नहीं हैं।

फिर हम यह 32SC1 में तब्दील:

marker32 = np.int32(marker) 

3 - अंत में हम वाटरशेड लागू और परिणाम वापस परिवर्तित uint8 छवि में:

cv2.watershed(img,marker32) 
m = cv2.convertScaleAbs(marker32) 

मीटर:

enter image description here

- हम सीमा इसे ठीक से मुखौटा पाने के लिए और इनपुट छवि के साथ bitwise_and करते हैं:

ret,thresh = cv2.threshold(m,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 
res = cv2.bitwise_and(img,img,mask = thresh) 

रेस:

enter image description here

आशा है कि यह मदद करता है! !!

ARK

+4

इसे पुनः जांचने पर विचार करें, क्योंकि यह वाटरशेड का पूरी तरह से गलत उपयोग दिखा रहा है। – mmgp

+2

@mmgp: क्या आप सही हो सकते हैं। यह केवल कुकबुक में दिए गए मूल सी ++ कोड का एक अजगर संस्करण है, जो मार्कर सेट करने के लिए दिखाता है, आदि। मैंने सोचा कि यह फ्रेशर्स के लिए एक अच्छा नमूना होगा, जिसमें 'मुझे शामिल है'। वैसे भी, आपका जवाब बहुत अच्छा है। यह मेरे जवाब में क्या कमी है भरता है। –

+0

-1 एमएमजीपी टिप्पणी के अनुसार –

संबंधित मुद्दे