python एक छवि में शोर के नकली छोटे द्वीपों को हटाएं-पायथन ओपनसीवी



image opencv (1)

मैं अपनी कुछ छवियों से पृष्ठभूमि शोर से छुटकारा पाने की कोशिश कर रहा हूं। यह unfiltered छवि है।

फ़िल्टर करने के लिए, मैंने छवि में क्या रहना चाहिए इसका एक मुखौटा उत्पन्न करने के लिए इस कोड का उपयोग किया:

 element = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
 mask = cv2.erode(mask, element, iterations = 1)
 mask = cv2.dilate(mask, element, iterations = 1)
 mask = cv2.erode(mask, element)

इस कोड के साथ और जब मैं मूल छवि से अवांछित पिक्सल को मुखौटा करता हूं, तो मुझे क्या मिलता है:

जैसा कि आप देख सकते हैं, मध्य क्षेत्र में सभी छोटे बिंदु चले गए हैं, लेकिन घने क्षेत्र से आने वाले बहुत से लोग भी चले गए हैं। फ़िल्टरिंग को कम करने के लिए, मैंने getStructuringElement() के दूसरे पैरामीटर को बदलने की कोशिश की (1,1) लेकिन ऐसा करने से मुझे पहली छवि मिलती है जैसे कि कुछ भी फ़िल्टर नहीं किया गया है।

क्या कोई तरीका है जहां मैं इन 2 चरम सीमाओं के बीच कुछ फ़िल्टर लागू कर सकता हूं?

इसके अलावा, क्या कोई मुझे बता सकता है कि getStructuringElement() क्या करता है? एक "संरचना तत्व" क्या है? यह क्या करता है और इसका आकार (दूसरा पैरामीटर) फ़िल्टरिंग के स्तर को कैसे प्रभावित करता है?


आपके बहुत सारे प्रश्न इस तथ्य से निकलते हैं कि आप निश्चित नहीं हैं कि मॉर्फोलॉजिकल इमेज प्रोसेसिंग कैसे काम करती है, लेकिन हम आपके संदेहों को आराम से रख सकते हैं। आप संरचनात्मक तत्व की तुलना करने के लिए "आधार आकार" के रूप में व्याख्या कर सकते हैं। संरचनात्मक तत्व में 1 एक पिक्सेल से मेल खाता है जिसे आप इस आकार में देखना चाहते हैं और 0 वह है जिसे आप अनदेखा करना चाहते हैं। विभिन्न आकार हैं, जैसे कि आयताकार (जैसा कि आपने MORPH_RECT साथ पता लगाया है), अंडाकार, परिपत्र आदि।

इस प्रकार, cv2.getStructuringElement आपके लिए एक संरचना तत्व देता है। पहला पैरामीटर आपके इच्छित प्रकार को निर्दिष्ट करता है और दूसरा पैरामीटर आपके इच्छित आकार को निर्दिष्ट करता है। आपके मामले में, आप 2 x 2 "आयताकार" चाहते हैं ... जो वास्तव में एक वर्ग है, लेकिन यह ठीक है।

एक अधिक बेस्टर्डिज्ड भावना में, आप संरचना तत्व का उपयोग करते हैं और बाएं से दाएं और अपनी छवि के ऊपर से नीचे तक स्कैन करते हैं और आप पिक्सेल पड़ोस लेते हैं। प्रत्येक पिक्सेल पड़ोस में बिल्कुल उस ब्याज की पिक्सेल पर केंद्र होता है जिसे आप देख रहे हैं। प्रत्येक पिक्सेल पड़ोस का आकार संरचना तत्व के समान आकार है।

कटाव

एक क्षरण के लिए, आप पिक्सेल पड़ोस में सभी पिक्सल की जांच करते हैं जो संरचना तत्व को छू रहे हैं। यदि प्रत्येक गैर-शून्य पिक्सेल एक स्ट्रक्चरिंग तत्व पिक्सेल को स्पर्श कर रहा है जो 1 है, तो इनपुट के संबंध में संबंधित केंद्र स्थिति में आउटपुट पिक्सेल 1 है। यदि कम से कम एक गैर-शून्य पिक्सेल है जो संरचनात्मक पिक्सेल को स्पर्श नहीं करता है वह 1 है, तो आउटपुट 0 है।

आयताकार संरचना तत्व के संदर्भ में, आपको यह सुनिश्चित करना होगा कि संरचनात्मक तत्व में प्रत्येक पिक्सेल आपकी छवि में एक पिक्सेल पड़ोस के लिए गैर-शून्य पिक्सेल को छू रहा हो। यदि यह नहीं है, तो आउटपुट 0 है, अन्यथा 1. यह प्रभावी रूप से शोर के छोटे नकली क्षेत्रों को समाप्त करता है और वस्तुओं के क्षेत्र को थोड़ा कम करता है।

आकार कारक जहां आयताकार बड़ा होता है, अधिक सिकुड़ने वाला होता है। संरचना तत्व का आकार आधारभूत आधार है जहां कोई आयताकार जो इस आयताकार संरचना तत्व से छोटा है, आप उन्हें फ़िल्टर किए जाने और आउटपुट में दिखाई देने पर विचार नहीं कर सकते हैं। असल में, 1 एक्स 1 आयताकार संरचना तत्व चुनना इनपुट छवि के समान ही है क्योंकि यह संरचना तत्व इसके अंदर सभी पिक्सेल फिट बैठता है क्योंकि पिक्सेल छवि में संभव जानकारी का सबसे छोटा प्रतिनिधित्व है।

फैलाव

Dilation क्षरण के विपरीत है। यदि कम से कम एक गैर-शून्य पिक्सेल है जो 1 है, तो संरचना तत्व में पिक्सेल को छूता है, तो आउटपुट 1 होता है, अन्यथा आउटपुट 0 होता है। आप इसे थोड़ा बढ़ते ऑब्जेक्ट क्षेत्रों के रूप में सोच सकते हैं और छोटे द्वीपों को बड़ा बना सकते हैं।

यहां आकार के साथ प्रभाव यह है कि संरचनात्मक तत्व जितना बड़ा होगा, वस्तुओं के बड़े हिस्से बड़े होंगे और अलग-अलग द्वीप बड़े होंगे।

आप जो कर रहे हैं वह एक कटाव के बाद पहले एक क्षरण है। यह एक उद्घाटन ऑपरेशन के रूप में जाना जाता है। इस ऑपरेशन का उद्देश्य शोर के छोटे द्वीपों को हटाने (अपनी कोशिश कर रहा है) अपनी छवि में बड़ी वस्तुओं के क्षेत्रों को बनाए रखना है। क्षरण उन द्वीपों को हटा देता है जबकि फैलाव बड़े वस्तुओं को उनके मूल आकार में वापस बढ़ाता है।

आप किसी कारण से फिर से एक क्षरण के साथ इसका पालन करते हैं, जिसे मैं समझ नहीं सकता, लेकिन यह ठीक है।

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

एक बार ऐसा करने के बाद, मैं मूल छवि के साथ किसी भी अतिरिक्त जानकारी को मास्क कर दूंगा ताकि छोटे द्वीपों को दूर करते समय आप बड़े क्षेत्रों को बरकरार रख सकें।

एक क्षरण के बाद एक क्षरण को चेन करने के बाद, या एक क्षरण के बाद एक फैलाव, cv2.morphologyEx उपयोग cv2.morphologyEx , जहां आप झंडे के रूप में MORPH_OPEN और MORPH_CLOSE निर्दिष्ट कर सकते हैं।

इस प्रकार, मैं व्यक्तिगत रूप से ऐसा करता हूं, मानते हैं कि आपकी छवि को spots.png कहा जाता है:

import cv2
import numpy as np

img = cv2.imread('spots.png')
img_bw = 255*(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) > 5).astype('uint8')

se1 = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
se2 = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
mask = cv2.morphologyEx(img_bw, cv2.MORPH_CLOSE, se1)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, se2)

mask = np.dstack([mask, mask, mask]) / 255
out = img * mask

cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('output.png', out)

उपर्युक्त कोड सुंदर आत्म-व्याख्यात्मक है। सबसे पहले, मैंने छवि में पढ़ा है और फिर मैं ऑब्जेक्ट पिक्सल के रूप में क्या माना जाता है इसका मुखौटा बनाने के लिए छवि को ग्रेस्केल और थ्रेसहोल्ड में 5 की तीव्रता के साथ परिवर्तित करता हूं। यह एक साफ छवि है और इसलिए 5 से बड़ा कुछ भी काम करता है। मॉर्फोलॉजी दिनचर्या के लिए, मुझे छवि को uint8 परिवर्तित करने और मास्क को 255 पर स्केल करने की आवश्यकता है। इसके बाद, हम दो संरचनात्मक तत्व बनाते हैं - एक जो समापन ऑपरेशन के लिए 5 x 5 आयत है और दूसरा जो 2 x 2 खोलने के लिए है ऑपरेशन। मैं थ्रेसहोल्ड छवि पर क्रमशः उद्घाटन और समापन संचालन के लिए cv2.morphologyEx चलाता cv2.morphologyEx

एक बार जब मैं ऐसा करता हूं, तो मैं मुखौटा ढेर करता हूं ताकि यह 3 डी मैट्रिक्स बन जाए और 255 तक विभाजित हो ताकि यह [0,1] का मुखौटा बन जाए और फिर हम इस मुखौटा को मूल छवि के साथ गुणा करें ताकि हम मूल पिक्सल को पकड़ सकें छवि के पीछे और मास्क आउटपुट से एक वास्तविक वस्तु माना जाता है।

शेष सिर्फ चित्रण के लिए है। मैं एक खिड़की में छवि दिखाता हूं, और मैं छवि को output.png नामक फ़ाइल में भी सहेजता हूं, और इसका उद्देश्य आपको यह दिखाने के लिए है कि इस पोस्ट में छवि कैसी दिखती है।

मैंने इसे प्राप्त किया:

ध्यान रखें कि यह सही नहीं है, लेकिन इससे पहले कि यह आपके पास पहले से बेहतर था। आपको एक अच्छा आउटपुट के रूप में विचार करने के लिए संरचना तत्व तत्वों के साथ खेलना होगा, लेकिन यह आपको शुरू करने के लिए निश्चित रूप से पर्याप्त है। सौभाग्य!

सी ++ संस्करण

ओपनसीवी का उपयोग कर सी ++ संस्करण में ऊपर लिखे गए कोड का अनुवाद करने के कुछ अनुरोध किए गए हैं। मैं अंततः कोड के एक सी ++ संस्करण लिखने के लिए मिल गया है और यह ओपनसीवी 3.1.0 पर परीक्षण किया गया है। इसके लिए कोड नीचे है। जैसा कि आप देख सकते हैं, कोड पायथन संस्करण में दिखाई देने के समान ही है। हालांकि, मैंने मूल छवि की प्रतिलिपि पर cv::Mat::setTo उपयोग किया और जो भी अंतिम मास्क का हिस्सा नहीं था, सेट करें। यह पाइथन में तत्व-वार गुणा करने जैसा ही है।

#include <opencv2/opencv.hpp>

using namespace cv;

int main(int argc, char *argv[])
{
    // Read in the image
    Mat img = imread("spots.png", CV_LOAD_IMAGE_COLOR);

    // Convert to black and white
    Mat img_bw;
    cvtColor(img, img_bw, COLOR_BGR2GRAY);
    img_bw = img_bw > 5;

    // Define the structuring elements
    Mat se1 = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat se2 = getStructuringElement(MORPH_RECT, Size(2, 2));

    // Perform closing then opening
    Mat mask;
    morphologyEx(img_bw, mask, MORPH_CLOSE, se1);
    morphologyEx(mask, mask, MORPH_OPEN, se2);

    // Filter the output
    Mat out = img.clone();
    out.setTo(Scalar(0), mask == 0);

    // Show image and save
    namedWindow("Output", WINDOW_NORMAL);
    imshow("Output", out);
    waitKey(0);
    destroyWindow("Output");
    imwrite("output.png", out);
}

परिणाम पाइथन संस्करण में जो भी मिलता है, वही होना चाहिए।





filtering