क्रिसमस ट्री का पता कैसे लगाएं? [बन्द है]


382

निम्नलिखित छवियों में प्रदर्शित क्रिसमस पेड़ों का पता लगाने वाले एप्लिकेशन को लागू करने के लिए कौन सी छवि प्रसंस्करण तकनीकों का उपयोग किया जा सकता है?

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

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

आप इन छवियों में पेड़ों का पता लगाने के बारे में कैसे प्रोग्राम करेंगे?


3
क्या हमें प्रशिक्षण के लिए कुछ छवियों का उपयोग करने की अनुमति है, या सभी आपूर्ति की गई छवियों को सत्यापन के लिए उपयोग किया जाना चाहिए? किसी भी तरह से, शांत प्रतिस्पर्धा: डी
हेंस ओवेन

7
@karlphillip, क्या आप चाहते हैं कि हम इन चित्रों का उपयोग परीक्षण और प्रशिक्षण के लिए अन्य छवियों के लिए करें? यह सिर्फ इतना है कि यह स्पष्ट नहीं है कि प्रशिक्षण सेट क्या है।
गिल्लवी

16
@karlphillip: मेरी सलाह: "ओपन सोर्स" आवश्यकता को छोड़ दें। यह वास्तव में मायने नहीं रखता है कि आप किस भाषा / रूपरेखा का उपयोग करते हैं। इमेज-प्रोसेसिंग / कंप्यूटर-विजन एल्गोरिदम भाषा अज्ञेय हैं, इसलिए यदि आप इसे MATLAB में लिख सकते हैं, तो आप निश्चित रूप से इसे OpenCV या किसी अन्य फ्रेमवर्क को पसंद कर सकते हैं ... इसके अलावा मैं अभी भी स्पष्ट नहीं हूं कि आप प्रशिक्षण / परीक्षण छवियों पर क्या विचार करते हैं !
अमरो

2
@Klphillip थैंक्स हम सबको इस 'खोज' में योगदान देने के लिए धन्यवाद! यह कुछ घंटों को उत्पादक रूप से बिताने का एक शानदार अवसर है, लेकिन सबसे महत्वपूर्ण बात यह है कि एक ही समस्या के लिए कितने अलग-अलग दृष्टिकोण देखने को मिल सकते हैं ... आशा है कि आप इसे 1 जनवरी के लिए फिर से करेंगे (शायद एक 'बेपहियों की गाड़ी' सांता क्लॉस की चुनौती; ;-))
sepdek

2
ठीक है, मैंने प्रतियोगिता तत्वों को हटाने के लिए प्रश्न को पुन: प्रस्तुत किया। मुझे लगता है कि इसे अपने दम पर खड़े होने देना चाहिए।
ब्रैड लार्सन

जवाबों:


184

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

शीर्ष स्तर पर, मेरा दृष्टिकोण काफी सरल है और लगभग 3 चरणों में टूट सकता है। पहले मैं एक दहलीज (या वास्तव में, तार्किक "या" दो अलग और अलग थ्रेसहोल्ड) लागू करता हूं। अन्य उत्तरों में से कई के साथ, मैंने मान लिया कि क्रिसमस का पेड़ दृश्य में उज्जवल वस्तुओं में से एक होगा, इसलिए पहली दहलीज सिर्फ एक साधारण मोनोक्रोम चमक परीक्षण है; 0-255 पैमाने (जहां काला 0 है और सफेद 255 है) पर 220 से ऊपर के मान वाले किसी भी पिक्सेल को एक बाइनरी ब्लैक-एंड-व्हाइट छवि में सहेजा गया है। दूसरी दहलीज लाल और पीले रंग की रोशनी को देखने की कोशिश करती है, जो विशेष रूप से छह छवियों के ऊपरी बाएँ और निचले दाएं पेड़ों में प्रमुख हैं, और नीले-हरे रंग की पृष्ठभूमि के खिलाफ अच्छी तरह से बाहर खड़े होते हैं जो अधिकांश तस्वीरों में प्रचलित है। मैं आरजीबी छवि को एचएसवी स्पेस में परिवर्तित करता हूं, और आवश्यकता है कि रंग या तो 0.0-1.0 पैमाने पर 0.2 से कम है (लगभग पीले और हरे रंग के बीच की सीमा पर) या 0.95 से अधिक (बैंगनी और लाल रंग के बीच की सीमा के अनुरूप) और इसके अलावा मुझे चमकीले, संतृप्त रंगों की आवश्यकता है: संतृप्ति और मूल्य दोनों 0.7 से ऊपर होने चाहिए। दो थ्रेशोल्ड प्रक्रियाओं के परिणाम तार्किक रूप से "या" एक साथ होते हैं, और परिणामस्वरूप काले और सफेद बाइनरी छवियों के मैट्रिक्स को नीचे दिखाया गया है:

क्रिसमस के पेड़, एचएसवी और साथ ही मोनोक्रोम चमक पर थ्रेसहोल्ड के बाद

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

इस कार्य के लिए मैंने DBSCAN को चुना । डीबीएससीएन आमतौर पर अन्य क्लस्टरिंग एल्गोरिदम के सापेक्ष कैसे व्यवहार करता है, इसकी एक बहुत अच्छी दृश्य तुलना है, यहां उपलब्ध है । जैसा कि मैंने पहले कहा था, यह अनाकार आकृति के साथ अच्छा करता है। DBSCAN का आउटपुट, प्रत्येक क्लस्टर को एक अलग रंग में प्लॉट किया गया है, यहाँ दिखाया गया है:

DBSCAN क्लस्टरिंग आउटपुट

इस परिणाम को देखते समय कुछ बातों का ध्यान रखना चाहिए। पहला यह है कि DBSCAN को अपने व्यवहार को विनियमित करने के लिए उपयोगकर्ता को एक "निकटता" पैरामीटर सेट करने की आवश्यकता होती है, जो प्रभावी रूप से नियंत्रित करता है कि एल्गोरिथ्म के लिए एक नया बिंदु अलग क्लस्टर घोषित करने के बजाय एक परीक्षण बिंदु को एग्लोमेरेट करने के लिए अलग से अंक कैसे अलग होना चाहिए। पहले से मौजूद क्लस्टर। मैंने यह मान प्रत्येक छवि के विकर्ण के साथ आकार का 0.04 गुना निर्धारित किया है। चूंकि छवियाँ लगभग VGA से HD 1080 तक आकार में भिन्न होती हैं, इसलिए इस प्रकार का स्केल-रिलेटिव डेफिनेशन महत्वपूर्ण है।

ध्यान देने योग्य एक और बात यह है कि DBSCAN एल्गोरिथ्म जैसा कि इसे scikit-learn में लागू किया गया है, इसमें मेमोरी सीमाएँ हैं जो इस नमूने में कुछ बड़ी छवियों के लिए काफी चुनौतीपूर्ण हैं। इसलिए, कुछ बड़ी छवियों के लिए, मुझे वास्तव में इस सीमा के भीतर रहने के लिए "डिकिमेट" (यानी, केवल प्रत्येक 3 या 4 पिक्सेल को बनाए रखना चाहिए और अन्य को छोड़ना होगा)। इस सुस्त प्रक्रिया के परिणामस्वरूप, कुछ अलग-अलग छवियों पर शेष व्यक्तिगत विरल पिक्सल्स को देखना मुश्किल है। इसलिए, केवल प्रदर्शन उद्देश्यों के लिए, उपरोक्त छवियों में रंग-कोडित पिक्सेल को केवल "थोड़ा" प्रभावी ढंग से "पतला" किया गया है ताकि वे बेहतर तरीके से खड़े हो सकें। यह पूरी तरह से कथा के लिए एक कॉस्मेटिक ऑपरेशन है; यद्यपि मेरे कोड में इस फैलाव का उल्लेख करने वाली टिप्पणियां हैं,

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

क्रिसमस पेड़ अपनी गणना सीमाओं के साथ

स्रोत कोड अजगर 2.7.6 के लिए लिखा है और उस पर निर्भर numpy , scipy , matplotlib और scikit सीखने । मैंने इसे दो भागों में विभाजित किया है। पहला भाग वास्तविक छवि प्रसंस्करण के लिए जिम्मेदार है:

from PIL import Image
import numpy as np
import scipy as sp
import matplotlib.colors as colors
from sklearn.cluster import DBSCAN
from math import ceil, sqrt

"""
Inputs:

    rgbimg:         [M,N,3] numpy array containing (uint, 0-255) color image

    hueleftthr:     Scalar constant to select maximum allowed hue in the
                    yellow-green region

    huerightthr:    Scalar constant to select minimum allowed hue in the
                    blue-purple region

    satthr:         Scalar constant to select minimum allowed saturation

    valthr:         Scalar constant to select minimum allowed value

    monothr:        Scalar constant to select minimum allowed monochrome
                    brightness

    maxpoints:      Scalar constant maximum number of pixels to forward to
                    the DBSCAN clustering algorithm

    proxthresh:     Proximity threshold to use for DBSCAN, as a fraction of
                    the diagonal size of the image

Outputs:

    borderseg:      [K,2,2] Nested list containing K pairs of x- and y- pixel
                    values for drawing the tree border

    X:              [P,2] List of pixels that passed the threshold step

    labels:         [Q,2] List of cluster labels for points in Xslice (see
                    below)

    Xslice:         [Q,2] Reduced list of pixels to be passed to DBSCAN

"""

def findtree(rgbimg, hueleftthr=0.2, huerightthr=0.95, satthr=0.7, 
             valthr=0.7, monothr=220, maxpoints=5000, proxthresh=0.04):

    # Convert rgb image to monochrome for
    gryimg = np.asarray(Image.fromarray(rgbimg).convert('L'))
    # Convert rgb image (uint, 0-255) to hsv (float, 0.0-1.0)
    hsvimg = colors.rgb_to_hsv(rgbimg.astype(float)/255)

    # Initialize binary thresholded image
    binimg = np.zeros((rgbimg.shape[0], rgbimg.shape[1]))
    # Find pixels with hue<0.2 or hue>0.95 (red or yellow) and saturation/value
    # both greater than 0.7 (saturated and bright)--tends to coincide with
    # ornamental lights on trees in some of the images
    boolidx = np.logical_and(
                np.logical_and(
                  np.logical_or((hsvimg[:,:,0] < hueleftthr),
                                (hsvimg[:,:,0] > huerightthr)),
                                (hsvimg[:,:,1] > satthr)),
                                (hsvimg[:,:,2] > valthr))
    # Find pixels that meet hsv criterion
    binimg[np.where(boolidx)] = 255
    # Add pixels that meet grayscale brightness criterion
    binimg[np.where(gryimg > monothr)] = 255

    # Prepare thresholded points for DBSCAN clustering algorithm
    X = np.transpose(np.where(binimg == 255))
    Xslice = X
    nsample = len(Xslice)
    if nsample > maxpoints:
        # Make sure number of points does not exceed DBSCAN maximum capacity
        Xslice = X[range(0,nsample,int(ceil(float(nsample)/maxpoints)))]

    # Translate DBSCAN proximity threshold to units of pixels and run DBSCAN
    pixproxthr = proxthresh * sqrt(binimg.shape[0]**2 + binimg.shape[1]**2)
    db = DBSCAN(eps=pixproxthr, min_samples=10).fit(Xslice)
    labels = db.labels_.astype(int)

    # Find the largest cluster (i.e., with most points) and obtain convex hull   
    unique_labels = set(labels)
    maxclustpt = 0
    for k in unique_labels:
        class_members = [index[0] for index in np.argwhere(labels == k)]
        if len(class_members) > maxclustpt:
            points = Xslice[class_members]
            hull = sp.spatial.ConvexHull(points)
            maxclustpt = len(class_members)
            borderseg = [[points[simplex,0], points[simplex,1]] for simplex
                          in hull.simplices]

    return borderseg, X, labels, Xslice

और दूसरा भाग एक उपयोगकर्ता-स्तरीय स्क्रिप्ट है जो पहली फ़ाइल को कॉल करता है और उपरोक्त सभी प्लॉट बनाता है:

#!/usr/bin/env python

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from findtree import findtree

# Image files to process
fname = ['nmzwj.png', 'aVZhC.png', '2K9EF.png',
         'YowlH.png', '2y4o5.png', 'FWhSP.png']

# Initialize figures
fgsz = (16,7)        
figthresh = plt.figure(figsize=fgsz, facecolor='w')
figclust  = plt.figure(figsize=fgsz, facecolor='w')
figcltwo  = plt.figure(figsize=fgsz, facecolor='w')
figborder = plt.figure(figsize=fgsz, facecolor='w')
figthresh.canvas.set_window_title('Thresholded HSV and Monochrome Brightness')
figclust.canvas.set_window_title('DBSCAN Clusters (Raw Pixel Output)')
figcltwo.canvas.set_window_title('DBSCAN Clusters (Slightly Dilated for Display)')
figborder.canvas.set_window_title('Trees with Borders')

for ii, name in zip(range(len(fname)), fname):
    # Open the file and convert to rgb image
    rgbimg = np.asarray(Image.open(name))

    # Get the tree borders as well as a bunch of other intermediate values
    # that will be used to illustrate how the algorithm works
    borderseg, X, labels, Xslice = findtree(rgbimg)

    # Display thresholded images
    axthresh = figthresh.add_subplot(2,3,ii+1)
    axthresh.set_xticks([])
    axthresh.set_yticks([])
    binimg = np.zeros((rgbimg.shape[0], rgbimg.shape[1]))
    for v, h in X:
        binimg[v,h] = 255
    axthresh.imshow(binimg, interpolation='nearest', cmap='Greys')

    # Display color-coded clusters
    axclust = figclust.add_subplot(2,3,ii+1) # Raw version
    axclust.set_xticks([])
    axclust.set_yticks([])
    axcltwo = figcltwo.add_subplot(2,3,ii+1) # Dilated slightly for display only
    axcltwo.set_xticks([])
    axcltwo.set_yticks([])
    axcltwo.imshow(binimg, interpolation='nearest', cmap='Greys')
    clustimg = np.ones(rgbimg.shape)    
    unique_labels = set(labels)
    # Generate a unique color for each cluster 
    plcol = cm.rainbow_r(np.linspace(0, 1, len(unique_labels)))
    for lbl, pix in zip(labels, Xslice):
        for col, unqlbl in zip(plcol, unique_labels):
            if lbl == unqlbl:
                # Cluster label of -1 indicates no cluster membership;
                # override default color with black
                if lbl == -1:
                    col = [0.0, 0.0, 0.0, 1.0]
                # Raw version
                for ij in range(3):
                    clustimg[pix[0],pix[1],ij] = col[ij]
                # Dilated just for display
                axcltwo.plot(pix[1], pix[0], 'o', markerfacecolor=col, 
                    markersize=1, markeredgecolor=col)
    axclust.imshow(clustimg)
    axcltwo.set_xlim(0, binimg.shape[1]-1)
    axcltwo.set_ylim(binimg.shape[0], -1)

    # Plot original images with read borders around the trees
    axborder = figborder.add_subplot(2,3,ii+1)
    axborder.set_axis_off()
    axborder.imshow(rgbimg, interpolation='nearest')
    for vseg, hseg in borderseg:
        axborder.plot(hseg, vseg, 'r-', lw=3)
    axborder.set_xlim(0, binimg.shape[1]-1)
    axborder.set_ylim(binimg.shape[0], -1)

plt.show()

@ lennon310 का समाधान क्लस्टरिंग है। (k- साधन)
user3054997

1
@stachyra मैंने अपने सरल लोगों को प्रस्तावित करने से पहले इस दृष्टिकोण के बारे में भी सोचा। मुझे लगता है कि यह अन्य मामलों में भी अच्छे परिणाम देने के लिए विस्तारित और सामान्यीकृत होने की काफी संभावना है। आप क्लस्टरिंग के लिए तंत्रिका जाल के साथ प्रयोग कर सकते हैं। SOM या न्यूरल गैस जैसी कोई चीज उत्कृष्ट काम करेगी। फिर भी, महान प्रस्ताव और मुझसे अंगूठे!
सीपडेक

4
@ फस्ट और रयान कार्लसन: धन्यवाद, दोस्तों! हां, मैं मानता हूं कि अपवोट सिस्टम, जबकि यह 2 या 3 संक्षिप्त उत्तरों के बीच एक दूसरे के कुछ घंटों के भीतर प्रस्तुत करने के लिए अच्छी तरह से काम करता है, गंभीर पूर्वाग्रह होते हैं, जब यह लंबे समय तक जवाब देने वाले दावों के साथ आता है, जो समय की विस्तारित अवधि में खेलते हैं। । एक बात के लिए, आरंभिक प्रस्तुतियाँ सार्वजनिक समीक्षा के लिए बाद में उपलब्ध होने से पहले ही जमा होने लगती हैं। और यदि उत्तर सभी लम्बे हैं, तो जैसे ही कोई एक मामूली सी लीड स्थापित करता है, अक्सर एक "बैंडवगन प्रभाव" होता है क्योंकि लोग केवल बाकी को पढ़ने के लिए परेशान किए बिना पहले वाले को उभारते हैं।
stachyra

2
@stachyra महान समाचार मित्र! हार्दिक बधाई और यह आपके नए साल की शुरुआत हो सकती है!
सीपडेक

1
@ lennon310: मैंने इस समस्या पर अभी तक एक स्थानीय अधिकतम खोज फ़िल्टर की कोशिश नहीं की है, लेकिन यदि आप इसे स्वयं तलाशना चाहते हैं, तो स्काईपी में यह भी शामिल है । इस परियोजना के लिए मेरा पायथन स्रोत कोड इतना छोटा था कि मैं वास्तव में इसका १००% प्रकाशित करने में सक्षम था; वस्तुतः आपको बस इतना करना होगा कि मेरे दो कोड स्निपेट को अलग-अलग .py फ़ाइलों में कॉपी-पेस्ट करें और फिर scipy.ndimage.filters.maximum_filter()उसी स्थान पर कॉल को प्रतिस्थापित करें जहां मैंने थ्रेशोल्ड का उपयोग किया था।
stachyra

145

संपादित करें नोट: मैंने इस पोस्ट को (i) प्रत्येक पेड़ की छवि को व्यक्तिगत रूप से संसाधित किया, जैसा कि आवश्यकताओं में अनुरोध किया गया है, (ii) परिणाम की गुणवत्ता में सुधार करने के लिए वस्तु चमक और आकार दोनों पर विचार करने के लिए।


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

पहला कदम रंग थ्रेसहोल्डिंग है। यहाँ उद्देश्य महत्वपूर्ण चमक के साथ वस्तुओं पर विश्लेषण को केंद्रित करना है।

आउटपुट चित्र:

सोर्स कोड:

public class ChristmasTree {

private MarvinImagePlugin fill = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.fill.boundaryFill");
private MarvinImagePlugin threshold = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.thresholding");
private MarvinImagePlugin invert = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.invert");
private MarvinImagePlugin dilation = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.morphological.dilation");

public ChristmasTree(){
    MarvinImage tree;

    // Iterate each image
    for(int i=1; i<=6; i++){
        tree = MarvinImageIO.loadImage("./res/trees/tree"+i+".png");

        // 1. Threshold
        threshold.setAttribute("threshold", 200);
        threshold.process(tree.clone(), tree);
    }
}
public static void main(String[] args) {
    new ChristmasTree();
}
}

दूसरे चरण में, आकृति बनाने के लिए छवि के सबसे चमकीले बिंदुओं को फैलाया जाता है। इस प्रक्रिया का परिणाम महत्वपूर्ण चमक के साथ वस्तुओं का संभावित आकार है। बाढ़ भराव विभाजन को लागू करने, डिस्कनेक्ट किए गए आकृतियों का पता लगाया जाता है।

आउटपुट चित्र:

सोर्स कोड:

public class ChristmasTree {

private MarvinImagePlugin fill = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.fill.boundaryFill");
private MarvinImagePlugin threshold = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.thresholding");
private MarvinImagePlugin invert = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.invert");
private MarvinImagePlugin dilation = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.morphological.dilation");

public ChristmasTree(){
    MarvinImage tree;

    // Iterate each image
    for(int i=1; i<=6; i++){
        tree = MarvinImageIO.loadImage("./res/trees/tree"+i+".png");

        // 1. Threshold
        threshold.setAttribute("threshold", 200);
        threshold.process(tree.clone(), tree);

        // 2. Dilate
        invert.process(tree.clone(), tree);
        tree = MarvinColorModelConverter.rgbToBinary(tree, 127);
        MarvinImageIO.saveImage(tree, "./res/trees/new/tree_"+i+"threshold.png");
        dilation.setAttribute("matrix", MarvinMath.getTrueMatrix(50, 50));
        dilation.process(tree.clone(), tree);
        MarvinImageIO.saveImage(tree, "./res/trees/new/tree_"+1+"_dilation.png");
        tree = MarvinColorModelConverter.binaryToRgb(tree);

        // 3. Segment shapes
        MarvinImage trees2 = tree.clone();
        fill(tree, trees2);
        MarvinImageIO.saveImage(trees2, "./res/trees/new/tree_"+i+"_fill.png");
}

private void fill(MarvinImage imageIn, MarvinImage imageOut){
    boolean found;
    int color= 0xFFFF0000;

    while(true){
        found=false;

        Outerloop:
        for(int y=0; y<imageIn.getHeight(); y++){
            for(int x=0; x<imageIn.getWidth(); x++){
                if(imageOut.getIntComponent0(x, y) == 0){
                    fill.setAttribute("x", x);
                    fill.setAttribute("y", y);
                    fill.setAttribute("color", color);
                    fill.setAttribute("threshold", 120);
                    fill.process(imageIn, imageOut);
                    color = newColor(color);

                    found = true;
                    break Outerloop;
                }
            }
        }

        if(!found){
            break;
        }
    }

}

private int newColor(int color){
    int red = (color & 0x00FF0000) >> 16;
    int green = (color & 0x0000FF00) >> 8;
    int blue = (color & 0x000000FF);

    if(red <= green && red <= blue){
        red+=5;
    }
    else if(green <= red && green <= blue){
        green+=5;
    }
    else{
        blue+=5;
    }

    return 0xFF000000 + (red << 16) + (green << 8) + blue;
}

public static void main(String[] args) {
    new ChristmasTree();
}
}

जैसा कि आउटपुट छवि में दिखाया गया है, कई आकृतियों का पता लगाया गया था। इस समस्या में, छवियों में बस कुछ उज्ज्वल बिंदु हैं। हालांकि, इस दृष्टिकोण को अधिक जटिल परिदृश्यों से निपटने के लिए लागू किया गया था।

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

सोर्स कोड:

private int[] detectTrees(MarvinImage image){
    HashSet<Integer> analysed = new HashSet<Integer>();
    boolean found;
    while(true){
        found = false;
        for(int y=0; y<image.getHeight(); y++){
            for(int x=0; x<image.getWidth(); x++){
                int color = image.getIntColor(x, y);

                if(!analysed.contains(color)){
                    if(isTree(image, color)){
                        return getObjectRect(image, color);
                    }

                    analysed.add(color);
                    found=true;
                }
            }
        }

        if(!found){
            break;
        }
    }
    return null;
}

private boolean isTree(MarvinImage image, int color){

    int mass[][] = new int[image.getHeight()][2];
    int yStart=-1;
    int xStart=-1;
    for(int y=0; y<image.getHeight(); y++){
        int mc = 0;
        int xs=-1;
        int xe=-1;
        for(int x=0; x<image.getWidth(); x++){
            if(image.getIntColor(x, y) == color){
                mc++;

                if(yStart == -1){
                    yStart=y;
                    xStart=x;
                }

                if(xs == -1){
                    xs = x;
                }
                if(x > xe){
                    xe = x;
                }
            }
        }
        mass[y][0] = xs;
        mass[y][3] = xe;
        mass[y][4] = mc;    
    }

    int validLines=0;
    for(int y=0; y<image.getHeight(); y++){
        if
        ( 
            mass[y][5] > 0 &&
            Math.abs(((mass[y][0]+mass[y][6])/2)-xStart) <= 50 &&
            mass[y][7] >= (mass[yStart][8] + (y-yStart)*0.3) &&
            mass[y][9] <= (mass[yStart][10] + (y-yStart)*1.5)
        )
        {
            validLines++;
        }
    }

    if(validLines > 100){
        return true;
    }
    return false;
}

अंत में, त्रिकोण के समान प्रत्येक आकृति की स्थिति और महत्वपूर्ण चमक के साथ, इस मामले में एक क्रिसमस ट्री, मूल छवि में हाइलाइट किया गया है, जैसा कि नीचे दिखाया गया है।

अंतिम आउटपुट चित्र:

अंतिम स्रोत कोड:

public class ChristmasTree {

private MarvinImagePlugin fill = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.fill.boundaryFill");
private MarvinImagePlugin threshold = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.thresholding");
private MarvinImagePlugin invert = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.invert");
private MarvinImagePlugin dilation = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.morphological.dilation");

public ChristmasTree(){
    MarvinImage tree;

    // Iterate each image
    for(int i=1; i<=6; i++){
        tree = MarvinImageIO.loadImage("./res/trees/tree"+i+".png");

        // 1. Threshold
        threshold.setAttribute("threshold", 200);
        threshold.process(tree.clone(), tree);

        // 2. Dilate
        invert.process(tree.clone(), tree);
        tree = MarvinColorModelConverter.rgbToBinary(tree, 127);
        MarvinImageIO.saveImage(tree, "./res/trees/new/tree_"+i+"threshold.png");
        dilation.setAttribute("matrix", MarvinMath.getTrueMatrix(50, 50));
        dilation.process(tree.clone(), tree);
        MarvinImageIO.saveImage(tree, "./res/trees/new/tree_"+1+"_dilation.png");
        tree = MarvinColorModelConverter.binaryToRgb(tree);

        // 3. Segment shapes
        MarvinImage trees2 = tree.clone();
        fill(tree, trees2);
        MarvinImageIO.saveImage(trees2, "./res/trees/new/tree_"+i+"_fill.png");

        // 4. Detect tree-like shapes
        int[] rect = detectTrees(trees2);

        // 5. Draw the result
        MarvinImage original = MarvinImageIO.loadImage("./res/trees/tree"+i+".png");
        drawBoundary(trees2, original, rect);
        MarvinImageIO.saveImage(original, "./res/trees/new/tree_"+i+"_out_2.jpg");
    }
}

private void drawBoundary(MarvinImage shape, MarvinImage original, int[] rect){
    int yLines[] = new int[6];
    yLines[0] = rect[1];
    yLines[1] = rect[1]+(int)((rect[3]/5));
    yLines[2] = rect[1]+((rect[3]/5)*2);
    yLines[3] = rect[1]+((rect[3]/5)*3);
    yLines[4] = rect[1]+(int)((rect[3]/5)*4);
    yLines[5] = rect[1]+rect[3];

    List<Point> points = new ArrayList<Point>();
    for(int i=0; i<yLines.length; i++){
        boolean in=false;
        Point startPoint=null;
        Point endPoint=null;
        for(int x=rect[0]; x<rect[0]+rect[2]; x++){

            if(shape.getIntColor(x, yLines[i]) != 0xFFFFFFFF){
                if(!in){
                    if(startPoint == null){
                        startPoint = new Point(x, yLines[i]);
                    }
                }
                in = true;
            }
            else{
                if(in){
                    endPoint = new Point(x, yLines[i]);
                }
                in = false;
            }
        }

        if(endPoint == null){
            endPoint = new Point((rect[0]+rect[2])-1, yLines[i]);
        }

        points.add(startPoint);
        points.add(endPoint);
    }

    drawLine(points.get(0).x, points.get(0).y, points.get(1).x, points.get(1).y, 15, original);
    drawLine(points.get(1).x, points.get(1).y, points.get(3).x, points.get(3).y, 15, original);
    drawLine(points.get(3).x, points.get(3).y, points.get(5).x, points.get(5).y, 15, original);
    drawLine(points.get(5).x, points.get(5).y, points.get(7).x, points.get(7).y, 15, original);
    drawLine(points.get(7).x, points.get(7).y, points.get(9).x, points.get(9).y, 15, original);
    drawLine(points.get(9).x, points.get(9).y, points.get(11).x, points.get(11).y, 15, original);
    drawLine(points.get(11).x, points.get(11).y, points.get(10).x, points.get(10).y, 15, original);
    drawLine(points.get(10).x, points.get(10).y, points.get(8).x, points.get(8).y, 15, original);
    drawLine(points.get(8).x, points.get(8).y, points.get(6).x, points.get(6).y, 15, original);
    drawLine(points.get(6).x, points.get(6).y, points.get(4).x, points.get(4).y, 15, original);
    drawLine(points.get(4).x, points.get(4).y, points.get(2).x, points.get(2).y, 15, original);
    drawLine(points.get(2).x, points.get(2).y, points.get(0).x, points.get(0).y, 15, original);
}

private void drawLine(int x1, int y1, int x2, int y2, int length, MarvinImage image){
    int lx1, lx2, ly1, ly2;
    for(int i=0; i<length; i++){
        lx1 = (x1+i >= image.getWidth() ? (image.getWidth()-1)-i: x1);
        lx2 = (x2+i >= image.getWidth() ? (image.getWidth()-1)-i: x2);
        ly1 = (y1+i >= image.getHeight() ? (image.getHeight()-1)-i: y1);
        ly2 = (y2+i >= image.getHeight() ? (image.getHeight()-1)-i: y2);

        image.drawLine(lx1+i, ly1, lx2+i, ly2, Color.red);
        image.drawLine(lx1, ly1+i, lx2, ly2+i, Color.red);
    }
}

private void fillRect(MarvinImage image, int[] rect, int length){
    for(int i=0; i<length; i++){
        image.drawRect(rect[0]+i, rect[1]+i, rect[2]-(i*2), rect[3]-(i*2), Color.red);
    }
}

private void fill(MarvinImage imageIn, MarvinImage imageOut){
    boolean found;
    int color= 0xFFFF0000;

    while(true){
        found=false;

        Outerloop:
        for(int y=0; y<imageIn.getHeight(); y++){
            for(int x=0; x<imageIn.getWidth(); x++){
                if(imageOut.getIntComponent0(x, y) == 0){
                    fill.setAttribute("x", x);
                    fill.setAttribute("y", y);
                    fill.setAttribute("color", color);
                    fill.setAttribute("threshold", 120);
                    fill.process(imageIn, imageOut);
                    color = newColor(color);

                    found = true;
                    break Outerloop;
                }
            }
        }

        if(!found){
            break;
        }
    }

}

private int[] detectTrees(MarvinImage image){
    HashSet<Integer> analysed = new HashSet<Integer>();
    boolean found;
    while(true){
        found = false;
        for(int y=0; y<image.getHeight(); y++){
            for(int x=0; x<image.getWidth(); x++){
                int color = image.getIntColor(x, y);

                if(!analysed.contains(color)){
                    if(isTree(image, color)){
                        return getObjectRect(image, color);
                    }

                    analysed.add(color);
                    found=true;
                }
            }
        }

        if(!found){
            break;
        }
    }
    return null;
}

private boolean isTree(MarvinImage image, int color){

    int mass[][] = new int[image.getHeight()][11];
    int yStart=-1;
    int xStart=-1;
    for(int y=0; y<image.getHeight(); y++){
        int mc = 0;
        int xs=-1;
        int xe=-1;
        for(int x=0; x<image.getWidth(); x++){
            if(image.getIntColor(x, y) == color){
                mc++;

                if(yStart == -1){
                    yStart=y;
                    xStart=x;
                }

                if(xs == -1){
                    xs = x;
                }
                if(x > xe){
                    xe = x;
                }
            }
        }
        mass[y][0] = xs;
        mass[y][12] = xe;
        mass[y][13] = mc;   
    }

    int validLines=0;
    for(int y=0; y<image.getHeight(); y++){
        if
        ( 
            mass[y][14] > 0 &&
            Math.abs(((mass[y][0]+mass[y][15])/2)-xStart) <= 50 &&
            mass[y][16] >= (mass[yStart][17] + (y-yStart)*0.3) &&
            mass[y][18] <= (mass[yStart][19] + (y-yStart)*1.5)
        )
        {
            validLines++;
        }
    }

    if(validLines > 100){
        return true;
    }
    return false;
}

private int[] getObjectRect(MarvinImage image, int color){
    int x1=-1;
    int x2=-1;
    int y1=-1;
    int y2=-1;

    for(int y=0; y<image.getHeight(); y++){
        for(int x=0; x<image.getWidth(); x++){
            if(image.getIntColor(x, y) == color){

                if(x1 == -1 || x < x1){
                    x1 = x;
                }
                if(x2 == -1 || x > x2){
                    x2 = x;
                }
                if(y1 == -1 || y < y1){
                    y1 = y;
                }
                if(y2 == -1 || y > y2){
                    y2 = y;
                }
            }
        }
    }

    return new int[]{x1, y1, (x2-x1), (y2-y1)};
}

private int newColor(int color){
    int red = (color & 0x00FF0000) >> 16;
    int green = (color & 0x0000FF00) >> 8;
    int blue = (color & 0x000000FF);

    if(red <= green && red <= blue){
        red+=5;
    }
    else if(green <= red && green <= blue){
        green+=30;
    }
    else{
        blue+=30;
    }

    return 0xFF000000 + (red << 16) + (green << 8) + blue;
}

public static void main(String[] args) {
    new ChristmasTree();
}
}

इस दृष्टिकोण का लाभ तथ्य यह है कि यह संभवतः अन्य चमकदार वस्तुओं वाली छवियों के साथ काम करेगा क्योंकि यह ऑब्जेक्ट आकार का विश्लेषण करता है।

क्रिसमस की बधाई!


EDIT नोट 2

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

नीचे इस बिंदु को स्पष्ट करने के लिए एक परिणाम प्रस्तुत किया गया है:

इनपुट छवि

यहां छवि विवरण दर्ज करें

उत्पादन

यहां छवि विवरण दर्ज करें


2
यह तो दिलचस्प है। मुझे आशा है कि प्रत्येक छवि को व्यक्तिगत रूप से संसाधित करने पर आपको वही परिणाम मिल सकते हैं। मैंने इस प्रश्न का 4hrs पहले संपादन किया था ताकि आप इसे विशेष रूप से बता सकें। यदि आप इन परिणामों के साथ अपना उत्तर अपडेट कर सकते हैं तो यह बहुत बढ़िया होगा।
कर्लफिलिप

@Marvin आपके त्रिकोण का पता लगाने में, आपने द्रव्यमान के उतार-चढ़ाव को कैसे हैंडल किया? यह एक सख्त त्रिभुज नहीं है, द्रव्यमान में परिवर्तन के रूप में द्रव्यमान मोनो नहीं है
user3054997

2
@ user3054997: यह एक और बात है। जैसा कि मैंने पोस्ट किया है, एल्गोरिथ्म सख्त त्रिकोण आकृतियों के लिए नहीं चाहता है। यह प्रत्येक वस्तु का विश्लेषण करता है और एक पेड़ पर विचार करता है जो एक "साधारण" के साथ एक त्रिकोण जैसा दिखता है: वस्तु का द्रव्यमान y वृद्धि के रूप में बढ़ जाता है और प्रत्येक क्षैतिज वस्तु खंड के द्रव्यमान का केंद्र लगभग एक दूसरे के लिए केंद्रीकृत होता है। ।
गेब्रियल अम्ब्रोसियो अर्चनाजो

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

1
@sepdek यहाँ समाधानों की एक जोड़ी है जो वास्तव में मेरी तुलना में बहुत बेहतर है और वे अभी भी मेरे आधे हिस्से को प्राप्त कर रहे हैं। अन्य समाधानों से "प्रेरित होने" में कुछ भी गलत नहीं है। मैंने आपके समाधान भी देखे, मेरे पास आपके खिलाफ कहने के लिए कुछ भी नहीं है, आपने उन्हें मेरे बाद पोस्ट किया और मेरा "विचार" यह कहने के लिए इतना मूल नहीं था कि आपने मुझे कॉपी किया। लेकिन मार्विन केवल वही था जो मुझसे पहले पोस्ट किया गया था और संपादित किया गया समाधान है, मेरा उसी एल्गोरिथ्म का उपयोग करके देखने के बाद ... कम से कम वह कह सकता है "हाँ, मुझे आपका समाधान पसंद आया और मैंने इसका पुन: उपयोग किया" कुछ भी गलत नहीं है, यह सिर्फ है एक खेल।
smeso

75

यहाँ मेरा सरल और गूंगा समाधान है। यह इस धारणा पर आधारित है कि पेड़ तस्वीर में सबसे उज्ज्वल और बड़ी चीज होगी।

//g++ -Wall -pedantic -ansi -O2 -pipe -s -o christmas_tree christmas_tree.cpp `pkg-config --cflags --libs opencv`
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc,char *argv[])
{
    Mat original,tmp,tmp1;
    vector <vector<Point> > contours;
    Moments m;
    Rect boundrect;
    Point2f center;
    double radius, max_area=0,tmp_area=0;
    unsigned int j, k;
    int i;

    for(i = 1; i < argc; ++i)
    {
        original = imread(argv[i]);
        if(original.empty())
        {
            cerr << "Error"<<endl;
            return -1;
        }

        GaussianBlur(original, tmp, Size(3, 3), 0, 0, BORDER_DEFAULT);
        erode(tmp, tmp, Mat(), Point(-1, -1), 10);
        cvtColor(tmp, tmp, CV_BGR2HSV);
        inRange(tmp, Scalar(0, 0, 0), Scalar(180, 255, 200), tmp);

        dilate(original, tmp1, Mat(), Point(-1, -1), 15);
        cvtColor(tmp1, tmp1, CV_BGR2HLS);
        inRange(tmp1, Scalar(0, 185, 0), Scalar(180, 255, 255), tmp1);
        dilate(tmp1, tmp1, Mat(), Point(-1, -1), 10);

        bitwise_and(tmp, tmp1, tmp1);

        findContours(tmp1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
        max_area = 0;
        j = 0;
        for(k = 0; k < contours.size(); k++)
        {
            tmp_area = contourArea(contours[k]);
            if(tmp_area > max_area)
            {
                max_area = tmp_area;
                j = k;
            }
        }
        tmp1 = Mat::zeros(original.size(),CV_8U);
        approxPolyDP(contours[j], contours[j], 30, true);
        drawContours(tmp1, contours, j, Scalar(255,255,255), CV_FILLED);

        m = moments(contours[j]);
        boundrect = boundingRect(contours[j]);
        center = Point2f(m.m10/m.m00, m.m01/m.m00);
        radius = (center.y - (boundrect.tl().y))/4.0*3.0;
        Rect heightrect(center.x-original.cols/5, boundrect.tl().y, original.cols/5*2, boundrect.size().height);

        tmp = Mat::zeros(original.size(), CV_8U);
        rectangle(tmp, heightrect, Scalar(255, 255, 255), -1);
        circle(tmp, center, radius, Scalar(255, 255, 255), -1);

        bitwise_and(tmp, tmp1, tmp1);

        findContours(tmp1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
        max_area = 0;
        j = 0;
        for(k = 0; k < contours.size(); k++)
        {
            tmp_area = contourArea(contours[k]);
            if(tmp_area > max_area)
            {
                max_area = tmp_area;
                j = k;
            }
        }

        approxPolyDP(contours[j], contours[j], 30, true);
        convexHull(contours[j], contours[j]);

        drawContours(original, contours, j, Scalar(0, 0, 255), 3);

        namedWindow(argv[i], CV_WINDOW_NORMAL|CV_WINDOW_KEEPRATIO|CV_GUI_EXPANDED);
        imshow(argv[i], original);

        waitKey(0);
        destroyWindow(argv[i]);
    }

    return 0;
}

पहला कदम तस्वीर में सबसे उज्ज्वल पिक्सल का पता लगाना है, लेकिन हमें खुद पेड़ और बर्फ के बीच एक अंतर करना होगा जो इसकी रोशनी को दर्शाता है। यहां हम रंग कोडों पर एक बहुत ही सरल फिल्टर की अपील करने वाले बर्फ को बाहर करने की कोशिश करते हैं:

GaussianBlur(original, tmp, Size(3, 3), 0, 0, BORDER_DEFAULT);
erode(tmp, tmp, Mat(), Point(-1, -1), 10);
cvtColor(tmp, tmp, CV_BGR2HSV);
inRange(tmp, Scalar(0, 0, 0), Scalar(180, 255, 200), tmp);

तब हम हर "उज्ज्वल" पिक्सेल पाते हैं:

dilate(original, tmp1, Mat(), Point(-1, -1), 15);
cvtColor(tmp1, tmp1, CV_BGR2HLS);
inRange(tmp1, Scalar(0, 185, 0), Scalar(180, 255, 255), tmp1);
dilate(tmp1, tmp1, Mat(), Point(-1, -1), 10);

अंत में हम दो परिणामों में शामिल होते हैं:

bitwise_and(tmp, tmp1, tmp1);

अब हम सबसे बड़ी चमकीली वस्तु की तलाश करते हैं:

findContours(tmp1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
max_area = 0;
j = 0;
for(k = 0; k < contours.size(); k++)
{
    tmp_area = contourArea(contours[k]);
    if(tmp_area > max_area)
    {
        max_area = tmp_area;
        j = k;
    }
}
tmp1 = Mat::zeros(original.size(),CV_8U);
approxPolyDP(contours[j], contours[j], 30, true);
drawContours(tmp1, contours, j, Scalar(255,255,255), CV_FILLED);

अब हम लगभग कर चुके हैं, लेकिन बर्फ के कारण अभी भी कुछ अपूर्णता है। उन्हें काटने के लिए हम एक चक्र का उपयोग करके एक मुखौटा बनाएंगे और अवांछित टुकड़ों को हटाने के लिए एक पेड़ के आकार को अनुमानित करेंगे:

m = moments(contours[j]);
boundrect = boundingRect(contours[j]);
center = Point2f(m.m10/m.m00, m.m01/m.m00);
radius = (center.y - (boundrect.tl().y))/4.0*3.0;
Rect heightrect(center.x-original.cols/5, boundrect.tl().y, original.cols/5*2, boundrect.size().height);

tmp = Mat::zeros(original.size(), CV_8U);
rectangle(tmp, heightrect, Scalar(255, 255, 255), -1);
circle(tmp, center, radius, Scalar(255, 255, 255), -1);

bitwise_and(tmp, tmp1, tmp1);

अंतिम चरण हमारे पेड़ के समोच्च को ढूंढना है और इसे मूल चित्र पर खींचना है।

findContours(tmp1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
max_area = 0;
j = 0;
for(k = 0; k < contours.size(); k++)
{
    tmp_area = contourArea(contours[k]);
    if(tmp_area > max_area)
    {
        max_area = tmp_area;
        j = k;
    }
}

approxPolyDP(contours[j], contours[j], 30, true);
convexHull(contours[j], contours[j]);

drawContours(original, contours, j, Scalar(0, 0, 255), 3);

मुझे क्षमा करें लेकिन इस समय मेरा एक बुरा संबंध है इसलिए मेरे लिए चित्र अपलोड करना संभव नहीं है। मैं इसे बाद में करने की कोशिश करूंगा।

क्रिसमस की बधाई।

संपादित करें:

यहाँ अंतिम उत्पादन की कुछ तस्वीरें:


1
नमस्कार! सुनिश्चित करें कि आपका उत्तर सभी आवश्यकताओं का अनुसरण करता है: 6 इनपुट चित्र हैं और उत्तर में उनमें से प्रत्येक को संसाधित करने के परिणाम प्रदर्शित होने चाहिए;
कार्लफिलिप

नमस्ते! आप मेरे कार्यक्रम में सीएलआई के तर्क के रूप में फाइलनाम पास कर सकते हैं ./christmas_tree ./*.png:। वे जितने चाहें उतने हो सकते हैं, परिणाम किसी भी कुंजी को दबाने के बाद एक दिखाया जाएगा। क्या यह गलत है?
smeso

यह ठीक है, लेकिन आपको अभी भी छवियों को अपलोड करने और उन्हें अपने प्रश्न में साझा करने की आवश्यकता है ताकि धागे के दर्शक वास्तव में आपका परिणाम देख सकें । लोगों को यह देखने दें कि आपने क्या किया वोट पाने की संभावना में सुधार होगा;)
karlphillip

मैं इसके लिए एक समाधान खोजने की कोशिश कर रहा हूं, मुझे कुछ कनेक्टिविटी समस्याएं हैं।
smeso

2
महान! अब आप उन्हें निम्नलिखित कोड के साथ उत्तर के अंदर <img src="http://i.stack.imgur.com/nmzwj.png" width="210" height="150">
पुनर्विक्रय

60

मैंने Matlab R2007a में कोड लिखा था। मैंने क्रिस्मस ट्री को निकालने के लिए k-mean का उपयोग किया। मैं अपना मध्यवर्ती परिणाम केवल एक छवि के साथ दिखाऊंगा, और सभी छह के साथ अंतिम परिणाम।

सबसे पहले, मैंने आरजीबी स्पेस को लैब स्पेस पर मैप किया, जो इसके बी चैनल में लाल के विपरीत को बढ़ा सकता है:

colorTransform = makecform('srgb2lab');
I = applycform(I, colorTransform);
L = double(I(:,:,1));
a = double(I(:,:,2));
b = double(I(:,:,3));

यहां छवि विवरण दर्ज करें

रंग अंतरिक्ष में सुविधा के अलावा, मैंने बनावट विशेषता का भी उपयोग किया जो प्रत्येक पिक्सेल के बजाय पड़ोस के साथ प्रासंगिक है। यहां मैंने 3 मूल चैनलों (आर, जी, बी) से तीव्रता को रैखिक रूप से संयोजित किया। इस कारण से कि मैंने इस तरह से प्रारूपित किया है क्योंकि चित्र में क्रिसमस के पेड़ सभी पर लाल बत्तियाँ होती हैं, और कभी-कभी हरे / कभी-कभी नीले रंग की रोशनी भी होती है।

R=double(Irgb(:,:,1));
G=double(Irgb(:,:,2));
B=double(Irgb(:,:,3));
I0 = (3*R + max(G,B)-min(G,B))/2;

यहां छवि विवरण दर्ज करें

मैंने 3X3 के स्थानीय बाइनरी पैटर्न को लागू किया I0, थ्रेशोल्ड के रूप में केंद्र पिक्सेल का उपयोग किया, और थ्रेसहोल्ड के ऊपर औसत पिक्सेल तीव्रता मान और इसके नीचे माध्य मान के बीच अंतर की गणना करके इसके विपरीत प्राप्त किया।

I0_copy = zeros(size(I0));
for i = 2 : size(I0,1) - 1
    for j = 2 : size(I0,2) - 1
        tmp = I0(i-1:i+1,j-1:j+1) >= I0(i,j);
        I0_copy(i,j) = mean(mean(tmp.*I0(i-1:i+1,j-1:j+1))) - ...
            mean(mean(~tmp.*I0(i-1:i+1,j-1:j+1))); % Contrast
    end
end

यहां छवि विवरण दर्ज करें

चूंकि मेरे पास कुल 4 विशेषताएं हैं, मैं अपने क्लस्टरिंग विधि में K = 5 चुनूंगा। K- साधनों के लिए कोड नीचे दिखाया गया है (यह डॉ। एंड्रयू एनजी के मशीन लर्निंग कोर्स से है। मैंने इससे पहले कोर्स लिया था, और मैंने खुद कोड को उनके प्रोग्रामिंग असाइनमेंट में लिखा था)।

[centroids, idx] = runkMeans(X, initial_centroids, max_iters);
mask=reshape(idx,img_size(1),img_size(2));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [centroids, idx] = runkMeans(X, initial_centroids, ...
                                  max_iters, plot_progress)
   [m n] = size(X);
   K = size(initial_centroids, 1);
   centroids = initial_centroids;
   previous_centroids = centroids;
   idx = zeros(m, 1);

   for i=1:max_iters    
      % For each example in X, assign it to the closest centroid
      idx = findClosestCentroids(X, centroids);

      % Given the memberships, compute new centroids
      centroids = computeCentroids(X, idx, K);

   end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function idx = findClosestCentroids(X, centroids)
   K = size(centroids, 1);
   idx = zeros(size(X,1), 1);
   for xi = 1:size(X,1)
      x = X(xi, :);
      % Find closest centroid for x.
      best = Inf;
      for mui = 1:K
        mu = centroids(mui, :);
        d = dot(x - mu, x - mu);
        if d < best
           best = d;
           idx(xi) = mui;
        end
      end
   end 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function centroids = computeCentroids(X, idx, K)
   [m n] = size(X);
   centroids = zeros(K, n);
   for mui = 1:K
      centroids(mui, :) = sum(X(idx == mui, :)) / sum(idx == mui);
   end

चूंकि प्रोग्राम मेरे कंप्यूटर में बहुत धीमा चलता है, इसलिए मैंने सिर्फ 3 पुनरावृत्तियों को चलाया। आम तौर पर स्टॉप मानदंड (i) पुनरावृत्ति समय कम से कम 10, या (ii) सेंट्रोइड पर कोई परिवर्तन नहीं होता है। मेरे परीक्षण में, पुनरावृत्ति में वृद्धि से पृष्ठभूमि (आकाश और पेड़, आकाश और भवन, ...) अधिक सटीक रूप से भिन्न हो सकती है, लेकिन क्रिसमस के पेड़ के निष्कर्षण में भारी बदलाव नहीं दिखा। ध्यान दें कि k- साधन यादृच्छिक सेंट्रोइड आरंभीकरण के लिए प्रतिरक्षा नहीं है, इसलिए तुलना करने के लिए प्रोग्राम को कई बार चलाने की सिफारिश की जाती है।

K- साधनों के बाद, अधिकतम तीव्रता वाला लेबल क्षेत्र I0चुना गया था। और सीमाओं को निकालने के लिए सीमा अनुरेखण का उपयोग किया गया था। मेरे लिए, आखिरी क्रिसमस ट्री सबसे मुश्किल है क्योंकि उस तस्वीर में कंट्रास्ट ज्यादा नहीं है क्योंकि वे पहले पांच में हैं। मेरी विधि में एक और मुद्दा यह है कि मैंने bwboundariesसीमा का पता लगाने के लिए माटलाब में फ़ंक्शन का उपयोग किया , लेकिन कभी-कभी आंतरिक सीमाएं भी शामिल होती हैं, जैसा कि आप 3rd, 5th, 6th परिणाम में देख सकते हैं। क्रिसमस के पेड़ों के भीतर का अंधेरा पक्ष न केवल प्रबुद्ध पक्ष के साथ जुड़ा होने में विफल रहता है, लेकिन वे बहुत सारे छोटे आंतरिक सीमाओं का पता लगाते हैं ( imfillबहुत सुधार नहीं करते हैं)। मेरे सभी एल्गोरिथ्म में अभी भी बहुत सुधार की जगह है।

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

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

क्रिसमस की बधाई !


2
जवाब के लिए धन्यवाद! मैं सिर्फ यह बताना चाहता था कि मतलाब खुला स्रोत नहीं है , लेकिन सिलाब है। मैं भी इस उत्तर को दूसरों के साथ प्रतिस्पर्धा करते हुए देखना पसंद करूंगा। ;)
कार्लफिलिप

6
धन्यवाद कार्ल। ऑक्टेव एक और खुला स्रोत सॉफ्टवेयर है जो लगभग उसी कोडिंग व्याकरण को मैटलैब के साथ साझा करता है: mathworks.fr/matlabcentral/answers/14399-gnu-octave-vs-matlab
lennon310

दिलचस्प है, मुझे नहीं पता था कि, धन्यवाद! क्या आपका कोड ऑक्टेव पर काम करता है?
कार्लफिलिप

मैंने अभी तक परीक्षण नहीं किया है, लेकिन मुझे लगता है कि यह कोई समस्या नहीं है :)
lennon310

@ lennon310 मुझे लगता है कि यदि आप सीमाओं को छोड़ देते हैं और उत्तल पतवार प्राप्त करते हैं तो आपको छेद की समस्या से छुटकारा मिल जाएगा। याद रखें कि उत्तल हल सबसे छोटा क्षेत्र है जिसमें एक सेट में सभी बिंदु शामिल हैं।
सिपदेक

57

यह पारंपरिक छवि प्रसंस्करण दृष्टिकोण का उपयोग करके मेरी अंतिम पोस्ट है ...

यहां मैं किसी भी तरह अपने दो अन्य प्रस्तावों को मिलाता हूं, और भी बेहतर परिणाम प्राप्त करता है । वास्तव में, मैं यह नहीं देख सकता कि ये परिणाम कैसे बेहतर हो सकते हैं (विशेषकर जब आप नकाबपोश छवियों को देखते हैं जो विधि उत्पन्न करती है)।

दृष्टिकोण के केंद्र में तीन प्रमुख मान्यताओं का संयोजन है :

  1. चित्र में वृक्ष क्षेत्रों में उच्च उतार-चढ़ाव होना चाहिए
  2. पेड़ क्षेत्रों में छवियों की तीव्रता अधिक होनी चाहिए
  3. पृष्ठभूमि क्षेत्रों में कम तीव्रता होनी चाहिए और अधिकतर नीले-ईश होना चाहिए

इन मान्यताओं को ध्यान में रखते हुए विधि निम्नानुसार काम करती है:

  1. छवियों को एचएसवी में परिवर्तित करें
  2. एक एलओजी फिल्टर के साथ वी चैनल को फ़िल्टर करें
  3. 'गतिविधि' मास्क A प्राप्त करने के लिए LoG फ़िल्टर की गई छवि पर हार्ड थ्रेसहोल्ड लागू करें
  4. तीव्रता मास्क बी प्राप्त करने के लिए वी चैनल पर हार्ड थ्रेसहोल्ड लागू करें
  5. पृष्ठभूमि मुखौटा सी में कम तीव्रता वाले नीले-ईश क्षेत्रों को पकड़ने के लिए एच चैनल थ्रॉल्डिंग लागू करें
  6. अंतिम मास्क प्राप्त करने के लिए मास्क का उपयोग करें
  7. क्षेत्रों को बढ़ाने और फैलाने वाले पिक्सेल को जोड़ने के लिए मास्क को पतला करें
  8. छोटे क्षेत्रों को हटा दें और अंतिम मुखौटा प्राप्त करें जो अंततः केवल पेड़ का प्रतिनिधित्व करेगा

यहां MATLAB में कोड है (फिर से, स्क्रिप्ट वर्तमान फ़ोल्डर में सभी jpg छवियों को लोड करता है और, फिर से, यह कोड का एक अनुकूलित टुकड़ा होने से बहुत दूर है):

% clear everything
clear;
pack;
close all;
close all hidden;
drawnow;
clc;

% initialization
ims=dir('./*.jpg');
imgs={};
images={}; 
blur_images={}; 
log_image={}; 
dilated_image={};
int_image={};
back_image={};
bin_image={};
measurements={};
box={};
num=length(ims);
thres_div = 3;

for i=1:num, 
    % load original image
    imgs{end+1}=imread(ims(i).name);

    % convert to HSV colorspace
    images{end+1}=rgb2hsv(imgs{i});

    % apply laplacian filtering and heuristic hard thresholding
    val_thres = (max(max(images{i}(:,:,3)))/thres_div);
    log_image{end+1} = imfilter( images{i}(:,:,3),fspecial('log')) > val_thres;

    % get the most bright regions of the image
    int_thres = 0.26*max(max( images{i}(:,:,3)));
    int_image{end+1} = images{i}(:,:,3) > int_thres;

    % get the most probable background regions of the image
    back_image{end+1} = images{i}(:,:,1)>(150/360) & images{i}(:,:,1)<(320/360) & images{i}(:,:,3)<0.5;

    % compute the final binary image by combining 
    % high 'activity' with high intensity
    bin_image{end+1} = logical( log_image{i}) & logical( int_image{i}) & ~logical( back_image{i});

    % apply morphological dilation to connect distonnected components
    strel_size = round(0.01*max(size(imgs{i})));        % structuring element for morphological dilation
    dilated_image{end+1} = imdilate( bin_image{i}, strel('disk',strel_size));

    % do some measurements to eliminate small objects
    measurements{i} = regionprops( logical( dilated_image{i}),'Area','BoundingBox');

    % iterative enlargement of the structuring element for better connectivity
    while length(measurements{i})>14 && strel_size<(min(size(imgs{i}(:,:,1)))/2),
        strel_size = round( 1.5 * strel_size);
        dilated_image{i} = imdilate( bin_image{i}, strel('disk',strel_size));
        measurements{i} = regionprops( logical( dilated_image{i}),'Area','BoundingBox');
    end

    for m=1:length(measurements{i})
        if measurements{i}(m).Area < 0.05*numel( dilated_image{i})
            dilated_image{i}( round(measurements{i}(m).BoundingBox(2):measurements{i}(m).BoundingBox(4)+measurements{i}(m).BoundingBox(2)),...
                round(measurements{i}(m).BoundingBox(1):measurements{i}(m).BoundingBox(3)+measurements{i}(m).BoundingBox(1))) = 0;
        end
    end
    % make sure the dilated image is the same size with the original
    dilated_image{i} = dilated_image{i}(1:size(imgs{i},1),1:size(imgs{i},2));
    % compute the bounding box
    [y,x] = find( dilated_image{i});
    if isempty( y)
        box{end+1}=[];
    else
        box{end+1} = [ min(x) min(y) max(x)-min(x)+1 max(y)-min(y)+1];
    end
end 

%%% additional code to display things
for i=1:num,
    figure;
    subplot(121);
    colormap gray;
    imshow( imgs{i});
    if ~isempty(box{i})
        hold on;
        rr = rectangle( 'position', box{i});
        set( rr, 'EdgeColor', 'r');
        hold off;
    end
    subplot(122);
    imshow( imgs{i}.*uint8(repmat(dilated_image{i},[1 1 3])));
end

परिणाम

परिणाम

उच्च संकल्प परिणाम अभी भी यहाँ उपलब्ध हैं!
अतिरिक्त चित्रों के साथ और भी अधिक प्रयोग यहाँ देखे जा सकते हैं।


1
उत्तम सामग्री! कृपया सुनिश्चित करें कि आपके अन्य उत्तर भी इस प्रारूप का अनुसरण करते हैं। इनाम के लिए प्रतिस्पर्धा करने के लिए आपको एक ओपन सोर्स तकनीक का उपयोग करना चाहिए , और दुर्भाग्य से मैटलैब उनमें से एक नहीं है। हालाँकि, SciLab और Octave हैं और वे समान सिंटैक्स और फ़ंक्शन प्रदान करते हैं। ;)
कार्लफिलिप

ऑक्टेव कोड समान है ...
sepdek

@karlphillip किसी तरह यह सवाल मतलूब टैग होने से खत्म हो गया। अगर खुला स्रोत वास्तव में जरूरी है तो मैं इसे हटाने की सिफारिश करूंगा।
डेनिस जहरुद्दीन

@sepdek बहुत अच्छा, शायद अंतिम चित्र में 'छेद' को शामिल करने के लिए अभी भी कुछ किया जा सकता है। (सभी पिक्सेल जोड़ें जो पूरी तरह से स्वीकृत पिक्सेल से घिरे हैं ?!)
डेनिस जहरुद्दीन

1
@ कार्लफिलिप थैंक्स मैन! मुझे खुशी है कि आपने मेरे दृष्टिकोण को दिलचस्प पाया। इसके अलावा मैं आपको सबसे सुरुचिपूर्ण समाधान का चयन करने के लिए बधाई देना चाहता हूं और सबसे अधिक वोटों के साथ नहीं !!!
१०

36

मेरा समाधान कदम:

  1. आर चैनल प्राप्त करें (आरजीबी से) - इस चैनल पर हमारे द्वारा किए जाने वाले सभी ऑपरेशन:

  2. ब्याज क्षेत्र बनाएँ (ROI)

    • शून्य मान के साथ थ्रेसहोल्ड R चैनल 149 (शीर्ष दाईं ओर छवि)

    • दिल का परिणाम क्षेत्र (मध्य बाएं छवि)

  3. गणना की गई रोटी में ईज का पता लगाएं। पेड़ में बहुत किनारे होते हैं (मध्य दाहिनी छवि)

    • परिणाम को पतला

    • बड़ी त्रिज्या के साथ इरोड (नीचे बाईं ओर छवि)

  4. सबसे बड़ी (क्षेत्र के अनुसार) वस्तु का चयन करें - यह परिणाम क्षेत्र है

  5. उत्तल (पेड़ उत्तल बहुभुज है) (नीचे दायीं छवि)

  6. बाउंडिंग बॉक्स (निचला दायाँ चित्र - ग्रैन बॉक्स)

क्रमशः: यहां छवि विवरण दर्ज करें

पहला परिणाम - सबसे सरल लेकिन ओपन सोर्स सॉफ़्टवेयर में नहीं - "एडेप्टिव विज़न स्टूडियो + अडेप्टिव विज़न लाइब्रेरी": यह ओपन सोर्स नहीं है लेकिन वास्तव में प्रोटोटाइप के लिए तेज़ है:

क्रिसमस ट्री (11 ब्लॉक) का पता लगाने के लिए संपूर्ण एल्गोरिथ्म: एवीएल समाधान

अगला कदम। हम खुला स्रोत समाधान चाहते हैं। AVL फ़िल्टर को OpenCV फ़िल्टर में बदलें: यहाँ मैंने कुछ बदलाव नहीं किए हैं जैसे एज डिटेक्शन cvCanny फ़िल्टर का उपयोग करते हैं, रोई का सम्मान करने के लिए मैंने किनारों की छवि के साथ क्षेत्र की छवि को गुणा किया, सबसे बड़ा तत्व जिसे मैंने खोजाContours + contourAA का उपयोग किया था, लेकिन विचार समान है।

https://www.youtube.com/watch?v=sfjB3MigLH0&index=1&list=UUpSRrkMHNHiLDXgylwhWNQQ

OpenCV समाधान

मैं अब मध्यवर्ती चरणों के साथ चित्र नहीं दिखा सकता क्योंकि मैं केवल 2 लिंक डाल सकता हूं।

ठीक है अब हम ओपनसोर्स फिल्टर का उपयोग करते हैं लेकिन यह अभी भी पूरे ओपन सोर्स नहीं है। अंतिम चरण - पोर्ट से c ++ कोड। मैंने संस्करण 2.4.4 में OpenCV का उपयोग किया

अंतिम c ++ कोड का परिणाम है: यहां छवि विवरण दर्ज करें

c ++ कोड भी काफी छोटा है:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include <algorithm>
using namespace cv;

int main()
{

    string images[6] = {"..\\1.png","..\\2.png","..\\3.png","..\\4.png","..\\5.png","..\\6.png"};

    for(int i = 0; i < 6; ++i)
    {
        Mat img, thresholded, tdilated, tmp, tmp1;
        vector<Mat> channels(3);

        img = imread(images[i]);
        split(img, channels);
        threshold( channels[2], thresholded, 149, 255, THRESH_BINARY);                      //prepare ROI - threshold
        dilate( thresholded, tdilated,  getStructuringElement( MORPH_RECT, Size(22,22) ) ); //prepare ROI - dilate
        Canny( channels[2], tmp, 75, 125, 3, true );    //Canny edge detection
        multiply( tmp, tdilated, tmp1 );    // set ROI

        dilate( tmp1, tmp, getStructuringElement( MORPH_RECT, Size(20,16) ) ); // dilate
        erode( tmp, tmp1, getStructuringElement( MORPH_RECT, Size(36,36) ) ); // erode

        vector<vector<Point> > contours, contours1(1);
        vector<Point> convex;
        vector<Vec4i> hierarchy;
        findContours( tmp1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

        //get element of maximum area
        //int bestID = std::max_element( contours.begin(), contours.end(), 
        //  []( const vector<Point>& A, const vector<Point>& B ) { return contourArea(A) < contourArea(B); } ) - contours.begin();

            int bestID = 0;
        int bestArea = contourArea( contours[0] );
        for( int i = 1; i < contours.size(); ++i )
        {
            int area = contourArea( contours[i] );
            if( area > bestArea )
            {
                bestArea  = area;
                bestID = i;
            }
        }

        convexHull( contours[bestID], contours1[0] ); 
        drawContours( img, contours1, 0, Scalar( 100, 100, 255 ), img.rows / 100, 8, hierarchy, 0, Point() );

        imshow("image", img );
        waitKey(0);
    }


    return 0;
}

कौन सा संकलक इस प्रोग्राम का निर्माण कर सकता है जिसमें कोई त्रुटि नहीं है?
कार्लफिलिप

मैंने इसे बनाने के लिए विजुअल स्टूडियो 2012 का उपयोग किया। आपको c ++ 11 के साथ c ++ कंपाइलर का उपयोग करना चाहिए।
एडम एफएन

मेरे पास मेरे निपटान में एक प्रणाली नहीं है। क्या आप std::max_element()कॉल को फिर से लिख सकते हैं ? मैं आपके उत्तर को भी पुरस्कृत करना चाहूंगा। मुझे लगता है कि मेरे पास 4.2 है।
कार्लफिलिप

ठीक है यह c ++ 11 फीचर है;) मैंने ऊपर दिए सोर्स कोड को बदल दिया है। कृपया अब कोशिश करो।
एडमएफ

ठीक है धन्यवाद। मैंने इसका परीक्षण किया और यह सुंदर है। जैसे ही यह प्रश्न फिर से खोला जाता है (अन्य उपयोगकर्ताओं को मेरी मदद करनी होगी) मैं आपको पुरस्कृत करने के लिए एक और इनाम निर्धारित कर सकता हूं। बधाई हो!
कर्लफिलिप

31

... एक और पुराने जमाने का हल - विशुद्ध रूप से एचएसवी प्रसंस्करण पर आधारित :

  1. छवियों को एचएसवी कलरस्पेस में परिवर्तित करें
  2. HSV में उत्तराधिकार के अनुसार मुखौटे बनाएँ (नीचे देखें)
  3. डिस्कनेक्ट किए गए क्षेत्रों को जोड़ने के लिए मुखौटा को रूपात्मक फैलाव लागू करें
  4. छोटे क्षेत्रों और क्षैतिज ब्लॉकों को छोड़ें (याद रखें कि पेड़ ऊर्ध्वाधर ब्लॉक हैं)
  5. बाउंडिंग बॉक्स की गणना करें

HSV प्रसंस्करण में उत्तराधिकार पर एक शब्द :

  1. 210 (320 डिग्री) के बीच हूस (एच) के साथ सब कुछ ब्लू-मैजेंटा के रूप में खारिज कर दिया जाता है जो कि पृष्ठभूमि में या गैर-प्रासंगिक क्षेत्रों में माना जाता है
  2. वैल्यूज़ (V) के साथ सब कुछ कम है कि 40% भी प्रासंगिक होने के लिए अंधेरा होने के रूप में खारिज कर दिया गया है

बेशक इस दृष्टिकोण को ठीक करने के लिए कई अन्य संभावनाओं के साथ प्रयोग किया जा सकता है ...

यहां ट्रिक करने के लिए MATLAB कोड है (चेतावनी: कोड अनुकूलित होने से बहुत दूर है !!! मैंने MATLAB प्रोग्रामिंग के लिए अनुशंसित तकनीकों का उपयोग नहीं किया है, जो प्रक्रिया में कुछ भी ट्रैक करने में सक्षम होने के लिए-यह बहुत अनुकूलित किया जा सकता है):

% clear everything
clear;
pack;
close all;
close all hidden;
drawnow;
clc;

% initialization
ims=dir('./*.jpg');
num=length(ims);

imgs={};
hsvs={}; 
masks={};
dilated_images={};
measurements={};
boxs={};

for i=1:num, 
    % load original image
    imgs{end+1} = imread(ims(i).name);
    flt_x_size = round(size(imgs{i},2)*0.005);
    flt_y_size = round(size(imgs{i},1)*0.005);
    flt = fspecial( 'average', max( flt_y_size, flt_x_size));
    imgs{i} = imfilter( imgs{i}, flt, 'same');
    % convert to HSV colorspace
    hsvs{end+1} = rgb2hsv(imgs{i});
    % apply a hard thresholding and binary operation to construct the mask
    masks{end+1} = medfilt2( ~(hsvs{i}(:,:,1)>(210/360) & hsvs{i}(:,:,1)<(320/360))&hsvs{i}(:,:,3)>0.4);
    % apply morphological dilation to connect distonnected components
    strel_size = round(0.03*max(size(imgs{i})));        % structuring element for morphological dilation
    dilated_images{end+1} = imdilate( masks{i}, strel('disk',strel_size));
    % do some measurements to eliminate small objects
    measurements{i} = regionprops( dilated_images{i},'Perimeter','Area','BoundingBox'); 
    for m=1:length(measurements{i})
        if (measurements{i}(m).Area < 0.02*numel( dilated_images{i})) || (measurements{i}(m).BoundingBox(3)>1.2*measurements{i}(m).BoundingBox(4))
            dilated_images{i}( round(measurements{i}(m).BoundingBox(2):measurements{i}(m).BoundingBox(4)+measurements{i}(m).BoundingBox(2)),...
                round(measurements{i}(m).BoundingBox(1):measurements{i}(m).BoundingBox(3)+measurements{i}(m).BoundingBox(1))) = 0;
        end
    end
    dilated_images{i} = dilated_images{i}(1:size(imgs{i},1),1:size(imgs{i},2));
    % compute the bounding box
    [y,x] = find( dilated_images{i});
    if isempty( y)
        boxs{end+1}=[];
    else
        boxs{end+1} = [ min(x) min(y) max(x)-min(x)+1 max(y)-min(y)+1];
    end

end 

%%% additional code to display things
for i=1:num,
    figure;
    subplot(121);
    colormap gray;
    imshow( imgs{i});
    if ~isempty(boxs{i})
        hold on;
        rr = rectangle( 'position', boxs{i});
        set( rr, 'EdgeColor', 'r');
        hold off;
    end
    subplot(122);
    imshow( imgs{i}.*uint8(repmat(dilated_images{i},[1 1 3])));
end

परिणाम:

परिणामों में मैं नकाबपोश छवि और बाउंडिंग बॉक्स दिखाता हूं। यहां छवि विवरण दर्ज करें


नमस्कार, उत्तर के लिए धन्यवाद। कृपया यह सुनिश्चित करने के लिए कि आपका उत्तर सभी निर्देशों का पालन करता है , आवश्यकताएँ अनुभाग पढ़ने के लिए कृपया एक क्षण ले लें। आप परिणामी छवियों को साझा करना भूल गए। ;)
कार्लफिलिप

2
@karlphillip sepdek में छवियों को साझा करने के लिए पर्याप्त प्रतिष्ठा नहीं है, मैंने उनके लिंक और निर्देशों के अनुसार छवियों को उत्तर निकाय में स्थानांतरित कर दिया। हालांकि यकीन नहीं है, कि वे सही हैं, इस हिस्से पर टिप्पणी करने के लिए स्वतंत्र महसूस करते हैं।
अल्को

@alko मुझे पता है, धन्यवाद। लेकिन आपके द्वारा साझा की गई कुछ छवियां इनपुट सेट में नहीं थीं । उत्तर को प्रश्न पर साझा की गई सभी 6 छवियों को संसाधित करने का परिणाम दिखाना होगा।
कार्लफिलिप

@karlphillip उसकी छवियां हैं, मेरी नहीं। कि बाह्य रूप से मेरा क्या मतलब है "टिप्पणी इस भाग";)
alko

2
मुद्दों के कारण के लिए खेद है ... मेरा इरादा नहीं। मैंने शुरुआती डेटासेट में सभी छवियों को शामिल किया है और इसे और भी अधिक बढ़ाया है ताकि यह साबित हो सके कि मेरी अवधारणा मजबूत है ...
sepdek

23

कुछ पुराने जमाने के इमेज प्रोसेसिंग दृष्टिकोण ...
यह विचार इस धारणा पर आधारित है कि चित्र आमतौर पर गहरे रंग और चिकनी पृष्ठभूमि (या कुछ मामलों में अग्रभूमि) पर प्रकाश वाले पेड़ों को चित्रित करते हैंरोशन पेड़ क्षेत्र अधिक "ऊर्जावान" है और उच्च तीव्रता है
प्रक्रिया इस प्रकार है:

  1. ग्रेवेल में बदलें
  2. सबसे "सक्रिय" क्षेत्रों को प्राप्त करने के लिए एलओजी फ़िल्टरिंग लागू करें
  3. सबसे उज्ज्वल क्षेत्रों को प्राप्त करने के लिए एक मंशा थ्रेसहोल्ड लागू करें
  4. प्रारंभिक मास्क प्राप्त करने के लिए पिछले 2 को मिलाएं
  5. क्षेत्रों में विस्तार और पड़ोसी घटकों को जोड़ने के लिए एक रूपात्मक फैलाव लागू करें
  6. अपने क्षेत्र के आकार के अनुसार छोटे उम्मीदवार क्षेत्रों को हटा दें

आपको जो मिलता है वह एक बाइनरी मास्क और प्रत्येक छवि के लिए एक बाउंडिंग बॉक्स है।

यहाँ इस भोले तकनीक का उपयोग कर परिणाम हैं: यहां छवि विवरण दर्ज करें

MATLAB पर कोड इस प्रकार है: कोड JPG छवियों के साथ एक फ़ोल्डर पर चलता है। सभी छवियों और रिटर्न का पता लगाया परिणामों को लोड करता है।

% clear everything
clear;
pack;
close all;
close all hidden;
drawnow;
clc;

% initialization
ims=dir('./*.jpg');
imgs={};
images={}; 
blur_images={}; 
log_image={}; 
dilated_image={};
int_image={};
bin_image={};
measurements={};
box={};
num=length(ims);
thres_div = 3;

for i=1:num, 
    % load original image
    imgs{end+1}=imread(ims(i).name);

    % convert to grayscale
    images{end+1}=rgb2gray(imgs{i});

    % apply laplacian filtering and heuristic hard thresholding
    val_thres = (max(max(images{i}))/thres_div);
    log_image{end+1} = imfilter( images{i},fspecial('log')) > val_thres;

    % get the most bright regions of the image
    int_thres = 0.26*max(max( images{i}));
    int_image{end+1} = images{i} > int_thres;

    % compute the final binary image by combining 
    % high 'activity' with high intensity
    bin_image{end+1} = log_image{i} .* int_image{i};

    % apply morphological dilation to connect distonnected components
    strel_size = round(0.01*max(size(imgs{i})));        % structuring element for morphological dilation
    dilated_image{end+1} = imdilate( bin_image{i}, strel('disk',strel_size));

    % do some measurements to eliminate small objects
    measurements{i} = regionprops( logical( dilated_image{i}),'Area','BoundingBox');
    for m=1:length(measurements{i})
        if measurements{i}(m).Area < 0.05*numel( dilated_image{i})
            dilated_image{i}( round(measurements{i}(m).BoundingBox(2):measurements{i}(m).BoundingBox(4)+measurements{i}(m).BoundingBox(2)),...
                round(measurements{i}(m).BoundingBox(1):measurements{i}(m).BoundingBox(3)+measurements{i}(m).BoundingBox(1))) = 0;
        end
    end
    % make sure the dilated image is the same size with the original
    dilated_image{i} = dilated_image{i}(1:size(imgs{i},1),1:size(imgs{i},2));
    % compute the bounding box
    [y,x] = find( dilated_image{i});
    if isempty( y)
        box{end+1}=[];
    else
        box{end+1} = [ min(x) min(y) max(x)-min(x)+1 max(y)-min(y)+1];
    end
end 

%%% additional code to display things
for i=1:num,
    figure;
    subplot(121);
    colormap gray;
    imshow( imgs{i});
    if ~isempty(box{i})
        hold on;
        rr = rectangle( 'position', box{i});
        set( rr, 'EdgeColor', 'r');
        hold off;
    end
    subplot(122);
    imshow( imgs{i}.*uint8(repmat(dilated_image{i},[1 1 3])));
end

परिणामी छवियों को अपलोड करना न भूलें, जैसे Faust ने किया था।
karlphillip

मैं यहाँ एक noob हूँ इसलिए मैं चित्र अपलोड नहीं कर सकता। कृपया मेरे विवरण में दिए गए लिंक पर परिणाम देखें।
सिपदेक

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

लिंक में अब सही चित्र शामिल हैं।
डेनिस जहीरुद्दीन

22

मैंने जो देखा है, उससे काफी अलग दृष्टिकोण का उपयोग करते हुए, मैंने एक बनाया लिपि जो क्रिसमस के पेड़ों को अपनी रोशनी से पहचानती है। परिणाम ist हमेशा एक सममित त्रिकोण होता है, और यदि आवश्यक संख्यात्मक मान जैसे कि पेड़ का कोण ("मोटापन")।

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

विभिन्न चरण हैं:

  • प्रत्येक पिक्सेल के लिए अतिरिक्त चमक (R + G + B) की गणना करें
  • प्रत्येक पिक्सेल के शीर्ष पर सभी 8 पड़ोसी पिक्सेल के इस मूल्य को जोड़ें
  • इस मूल्य के अनुसार सभी पिक्सेल रैंक करें (सबसे पहले) - मुझे पता है, वास्तव में सूक्ष्म नहीं है ...
  • इनमें से N चुनें, जो ऊपर से शुरू हो रहा है, जो बहुत पास हैं उन्हें छोड़ दें
  • इसे परिकलित करें इन शीर्ष एन (हमें पेड़ का अनुमानित केंद्र देता है)
  • चयनित प्रतिभाशाली लोगों में से सबसे ऊपरी प्रकाश के लिए एक चौड़ी खोज किरण में औसत दर्जे से ऊपर की ओर से शुरू करें (लोग बहुत ऊपर से कम से कम एक प्रकाश डालते हैं)
  • वहां से, 60 डिग्री बाएं और दाएं नीचे की ओर जाने वाली रेखाओं की कल्पना करें (क्रिसमस के पेड़ को वह वसा नहीं होना चाहिए)
  • उन 60 डिग्री तक घटाएं जब तक कि 20% चमकदार रोशनी इस त्रिकोण के बाहर न हो
  • त्रिकोण के बहुत नीचे प्रकाश का पता लगाएं, जिससे आपको पेड़ की निचली क्षैतिज सीमा मिलती है
  • किया हुआ

चिह्नों की व्याख्या:

  • पेड़ के केंद्र में बड़ा लाल क्रॉस: शीर्ष एन उज्ज्वल रोशनी का मेडियन
  • वहां से ऊपर की ओर बिंदीदार रेखा: पेड़ के शीर्ष के लिए "खोज बीम"
  • छोटा लाल क्रॉस: पेड़ के ऊपर
  • वास्तव में छोटे लाल पार: शीर्ष एन प्रतिभाशाली रोशनी के सभी
  • लाल त्रिकोण: D'uh!

सोर्स कोड:

<?php

ini_set('memory_limit', '1024M');

header("Content-type: image/png");

$chosenImage = 6;

switch($chosenImage){
    case 1:
        $inputImage     = imagecreatefromjpeg("nmzwj.jpg");
        break;
    case 2:
        $inputImage     = imagecreatefromjpeg("2y4o5.jpg");
        break;
    case 3:
        $inputImage     = imagecreatefromjpeg("YowlH.jpg");
        break;
    case 4:
        $inputImage     = imagecreatefromjpeg("2K9Ef.jpg");
        break;
    case 5:
        $inputImage     = imagecreatefromjpeg("aVZhC.jpg");
        break;
    case 6:
        $inputImage     = imagecreatefromjpeg("FWhSP.jpg");
        break;
    case 7:
        $inputImage     = imagecreatefromjpeg("roemerberg.jpg");
        break;
    default:
        exit();
}

// Process the loaded image

$topNspots = processImage($inputImage);

imagejpeg($inputImage);
imagedestroy($inputImage);

// Here be functions

function processImage($image) {
    $orange = imagecolorallocate($image, 220, 210, 60);
    $black = imagecolorallocate($image, 0, 0, 0);
    $red = imagecolorallocate($image, 255, 0, 0);

    $maxX = imagesx($image)-1;
    $maxY = imagesy($image)-1;

    // Parameters
    $spread = 1; // Number of pixels to each direction that will be added up
    $topPositions = 80; // Number of (brightest) lights taken into account
    $minLightDistance = round(min(array($maxX, $maxY)) / 30); // Minimum number of pixels between the brigtests lights
    $searchYperX = 5; // spread of the "search beam" from the median point to the top

    $renderStage = 3; // 1 to 3; exits the process early


    // STAGE 1
    // Calculate the brightness of each pixel (R+G+B)

    $maxBrightness = 0;
    $stage1array = array();

    for($row = 0; $row <= $maxY; $row++) {

        $stage1array[$row] = array();

        for($col = 0; $col <= $maxX; $col++) {

            $rgb = imagecolorat($image, $col, $row);
            $brightness = getBrightnessFromRgb($rgb);
            $stage1array[$row][$col] = $brightness;

            if($renderStage == 1){
                $brightnessToGrey = round($brightness / 765 * 256);
                $greyRgb = imagecolorallocate($image, $brightnessToGrey, $brightnessToGrey, $brightnessToGrey);
                imagesetpixel($image, $col, $row, $greyRgb);
            }

            if($brightness > $maxBrightness) {
                $maxBrightness = $brightness;
                if($renderStage == 1){
                    imagesetpixel($image, $col, $row, $red);
                }
            }
        }
    }
    if($renderStage == 1) {
        return;
    }


    // STAGE 2
    // Add up brightness of neighbouring pixels

    $stage2array = array();
    $maxStage2 = 0;

    for($row = 0; $row <= $maxY; $row++) {
        $stage2array[$row] = array();

        for($col = 0; $col <= $maxX; $col++) {
            if(!isset($stage2array[$row][$col])) $stage2array[$row][$col] = 0;

            // Look around the current pixel, add brightness
            for($y = $row-$spread; $y <= $row+$spread; $y++) {
                for($x = $col-$spread; $x <= $col+$spread; $x++) {

                    // Don't read values from outside the image
                    if($x >= 0 && $x <= $maxX && $y >= 0 && $y <= $maxY){
                        $stage2array[$row][$col] += $stage1array[$y][$x]+10;
                    }
                }
            }

            $stage2value = $stage2array[$row][$col];
            if($stage2value > $maxStage2) {
                $maxStage2 = $stage2value;
            }
        }
    }

    if($renderStage >= 2){
        // Paint the accumulated light, dimmed by the maximum value from stage 2
        for($row = 0; $row <= $maxY; $row++) {
            for($col = 0; $col <= $maxX; $col++) {
                $brightness = round($stage2array[$row][$col] / $maxStage2 * 255);
                $greyRgb = imagecolorallocate($image, $brightness, $brightness, $brightness);
                imagesetpixel($image, $col, $row, $greyRgb);
            }
        }
    }

    if($renderStage == 2) {
        return;
    }


    // STAGE 3

    // Create a ranking of bright spots (like "Top 20")
    $topN = array();

    for($row = 0; $row <= $maxY; $row++) {
        for($col = 0; $col <= $maxX; $col++) {

            $stage2Brightness = $stage2array[$row][$col];
            $topN[$col.":".$row] = $stage2Brightness;
        }
    }
    arsort($topN);

    $topNused = array();
    $topPositionCountdown = $topPositions;

    if($renderStage == 3){
        foreach ($topN as $key => $val) {
            if($topPositionCountdown <= 0){
                break;
            }

            $position = explode(":", $key);

            foreach($topNused as $usedPosition => $usedValue) {
                $usedPosition = explode(":", $usedPosition);
                $distance = abs($usedPosition[0] - $position[0]) + abs($usedPosition[1] - $position[1]);
                if($distance < $minLightDistance) {
                    continue 2;
                }
            }

            $topNused[$key] = $val;

            paintCrosshair($image, $position[0], $position[1], $red, 2);

            $topPositionCountdown--;

        }
    }


    // STAGE 4
    // Median of all Top N lights
    $topNxValues = array();
    $topNyValues = array();

    foreach ($topNused as $key => $val) {
        $position = explode(":", $key);
        array_push($topNxValues, $position[0]);
        array_push($topNyValues, $position[1]);
    }

    $medianXvalue = round(calculate_median($topNxValues));
    $medianYvalue = round(calculate_median($topNyValues));
    paintCrosshair($image, $medianXvalue, $medianYvalue, $red, 15);


    // STAGE 5
    // Find treetop

    $filename = 'debug.log';
    $handle = fopen($filename, "w");
    fwrite($handle, "\n\n STAGE 5");

    $treetopX = $medianXvalue;
    $treetopY = $medianYvalue;

    $searchXmin = $medianXvalue;
    $searchXmax = $medianXvalue;

    $width = 0;
    for($y = $medianYvalue; $y >= 0; $y--) {
        fwrite($handle, "\nAt y = ".$y);

        if(($y % $searchYperX) == 0) { // Modulo
            $width++;
            $searchXmin = $medianXvalue - $width;
            $searchXmax = $medianXvalue + $width;
            imagesetpixel($image, $searchXmin, $y, $red);
            imagesetpixel($image, $searchXmax, $y, $red);
        }

        foreach ($topNused as $key => $val) {
            $position = explode(":", $key); // "x:y"

            if($position[1] != $y){
                continue;
            }

            if($position[0] >= $searchXmin && $position[0] <= $searchXmax){
                $treetopX = $position[0];
                $treetopY = $y;
            }
        }

    }

    paintCrosshair($image, $treetopX, $treetopY, $red, 5);


    // STAGE 6
    // Find tree sides
    fwrite($handle, "\n\n STAGE 6");

    $treesideAngle = 60; // The extremely "fat" end of a christmas tree
    $treeBottomY = $treetopY;

    $topPositionsExcluded = 0;
    $xymultiplier = 0;
    while(($topPositionsExcluded < ($topPositions / 5)) && $treesideAngle >= 1){
        fwrite($handle, "\n\nWe're at angle ".$treesideAngle);
        $xymultiplier = sin(deg2rad($treesideAngle));
        fwrite($handle, "\nMultiplier: ".$xymultiplier);

        $topPositionsExcluded = 0;
        foreach ($topNused as $key => $val) {
            $position = explode(":", $key);
            fwrite($handle, "\nAt position ".$key);

            if($position[1] > $treeBottomY) {
                $treeBottomY = $position[1];
            }

            // Lights above the tree are outside of it, but don't matter
            if($position[1] < $treetopY){
                $topPositionsExcluded++;
                fwrite($handle, "\nTOO HIGH");
                continue;
            }

            // Top light will generate division by zero
            if($treetopY-$position[1] == 0) {
                fwrite($handle, "\nDIVISION BY ZERO");
                continue;
            }

            // Lights left end right of it are also not inside
            fwrite($handle, "\nLight position factor: ".(abs($treetopX-$position[0]) / abs($treetopY-$position[1])));
            if((abs($treetopX-$position[0]) / abs($treetopY-$position[1])) > $xymultiplier){
                $topPositionsExcluded++;
                fwrite($handle, "\n --- Outside tree ---");
            }
        }

        $treesideAngle--;
    }
    fclose($handle);

    // Paint tree's outline
    $treeHeight = abs($treetopY-$treeBottomY);
    $treeBottomLeft = 0;
    $treeBottomRight = 0;
    $previousState = false; // line has not started; assumes the tree does not "leave"^^

    for($x = 0; $x <= $maxX; $x++){
        if(abs($treetopX-$x) != 0 && abs($treetopX-$x) / $treeHeight > $xymultiplier){
            if($previousState == true){
                $treeBottomRight = $x;
                $previousState = false;
            }
            continue;
        }
        imagesetpixel($image, $x, $treeBottomY, $red);
        if($previousState == false){
            $treeBottomLeft = $x;
            $previousState = true;
        }
    }
    imageline($image, $treeBottomLeft, $treeBottomY, $treetopX, $treetopY, $red);
    imageline($image, $treeBottomRight, $treeBottomY, $treetopX, $treetopY, $red);


    // Print out some parameters

    $string = "Min dist: ".$minLightDistance." | Tree angle: ".$treesideAngle." deg | Tree bottom: ".$treeBottomY;

    $px     = (imagesx($image) - 6.5 * strlen($string)) / 2;
    imagestring($image, 2, $px, 5, $string, $orange);

    return $topN;
}

/**
 * Returns values from 0 to 765
 */
function getBrightnessFromRgb($rgb) {
    $r = ($rgb >> 16) & 0xFF;
    $g = ($rgb >> 8) & 0xFF;
    $b = $rgb & 0xFF;

    return $r+$r+$b;
}

function paintCrosshair($image, $posX, $posY, $color, $size=5) {
    for($x = $posX-$size; $x <= $posX+$size; $x++) {
        if($x>=0 && $x < imagesx($image)){
            imagesetpixel($image, $x, $posY, $color);
        }
    }
    for($y = $posY-$size; $y <= $posY+$size; $y++) {
        if($y>=0 && $y < imagesy($image)){
            imagesetpixel($image, $posX, $y, $color);
        }
    }
}

// From http://www.mdj.us/web-development/php-programming/calculating-the-median-average-values-of-an-array-with-php/
function calculate_median($arr) {
    sort($arr);
    $count = count($arr); //total numbers in array
    $middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value
    if($count % 2) { // odd number, middle is the median
        $median = $arr[$middleval];
    } else { // even number, calculate avg of 2 medians
        $low = $arr[$middleval];
        $high = $arr[$middleval+1];
        $median = (($low+$high)/2);
    }
    return $median;
}


?>

इमेजिस: ऊपरी बांया निचला केंद्र निचला बायां उपर से दाहिना ऊपरी केंद्र निचली दाईं ओर

बोनस: विकिपीडिया रोमरबर्ग http://commons.wikimedia.org/wiki/File:Weihnachtsbaum_R%C3%B6merberg.jpg


17

मैंने opencv के साथ अजगर का उपयोग किया।

मेरा एल्गोरिथ्म इस तरह है:

  1. पहले यह इमेज से रेड चैनल लेता है
  2. लाल चैनल पर थ्रेशोल्ड (न्यूनतम मान 200) लागू करें
  3. फिर मॉर्फोलॉजिकल ग्रैडिएंट लागू करें और फिर एक 'क्लोजिंग' (एरोशन के बाद फैलाव) करें
  4. फिर यह समतल में समोच्च को पाता है और यह सबसे लंबा समोच्च चुनता है।

नतीजा:

कोड:

import numpy as np
import cv2
import copy


def findTree(image,num):
    im = cv2.imread(image)
    im = cv2.resize(im, (400,250))
    gray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
    imf = copy.deepcopy(im)

    b,g,r = cv2.split(im)
    minR = 200
    _,thresh = cv2.threshold(r,minR,255,0)
    kernel = np.ones((25,5))
    dst = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel)
    dst = cv2.morphologyEx(dst, cv2.MORPH_CLOSE, kernel)

    contours = cv2.findContours(dst,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)[0]
    cv2.drawContours(im, contours,-1, (0,255,0), 1)

    maxI = 0
    for i in range(len(contours)):
        if len(contours[maxI]) < len(contours[i]):
            maxI = i

    img = copy.deepcopy(r)
    cv2.polylines(img,[contours[maxI]],True,(255,255,255),3)
    imf[:,:,2] = img

    cv2.imshow(str(num), imf)

def main():
    findTree('tree.jpg',1)
    findTree('tree2.jpg',2)
    findTree('tree3.jpg',3)
    findTree('tree4.jpg',4)
    findTree('tree5.jpg',5)
    findTree('tree6.jpg',6)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

यदि मैं कर्नेल को (25,5) से (10,5) में बदलता हूं तो मुझे सभी पेड़ों पर अच्छे परिणाम मिलते हैं, लेकिन नीचे का भाग, यहां छवि विवरण दर्ज करें

मेरा एल्गोरिथ्म मानता है कि पेड़ पर रोशनी है, और नीचे बाएं पेड़ में, ऊपर की रोशनी कम है और फिर अन्य।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.