কিভাবে একটি ক্রিসমাস ট্রি সনাক্ত? [বন্ধ]


382

নিম্নলিখিত চিত্রগুলিতে প্রদর্শিত ক্রিসমাস ট্রি সনাক্ত করে এমন কোনও অ্যাপ্লিকেশন বাস্তবায়নের জন্য কোন চিত্র প্রক্রিয়াকরণ কৌশল ব্যবহার করা যেতে পারে?

আমি সমাধানগুলি অনুসন্ধান করছি যা এই সমস্ত চিত্র নিয়ে কাজ করবে। অতএব, হ্যার ক্যাসকেড শ্রেণিবদ্ধ বা টেমপ্লেট ম্যাচিংয়ের প্রয়োজনীয় প্রশিক্ষণগুলি খুব আকর্ষণীয় নয়।

আমি এমন কিছু সন্ধান করছি যা কোনও প্রোগ্রামিং ভাষায় লেখা যেতে পারে , যতক্ষণ না এটি কেবল ওপেন সোর্স প্রযুক্তি ব্যবহার করে। এই প্রশ্নের সাথে ভাগ করা চিত্রগুলির সাথে সমাধানটি অবশ্যই পরীক্ষা করা উচিত। এখানে 6 টি ইনপুট চিত্র রয়েছে এবং উত্তরের মধ্যে তাদের প্রতিটি প্রসেসিংয়ের ফলাফল প্রদর্শন করা উচিত। অবশেষে, প্রতিটি আউটপুট চিত্রের জন্য অবশ্যই সনাক্ত করা গাছকে ঘিরে রেখার লাইন থাকতে হবে ।

আপনি এই চিত্রগুলিতে গাছগুলি সনাক্তকরণের জন্য কীভাবে যাবেন?


3
প্রশিক্ষণের জন্য আমাদের কিছু চিত্র ব্যবহার করার অনুমতি দেওয়া হয়েছে, বা সরবরাহকৃত সমস্ত চিত্রগুলি বৈধতার জন্য ব্যবহার করা উচিত? যেভাবেই হোক, দুর্দান্ত প্রতিযোগিতা: ডি
হ্যানস ওভ্রন

7
@ করলফিলিপ, আপনি কি চান আমাদের এই চিত্রগুলি পরীক্ষার জন্য এবং অন্যান্য চিত্রগুলি প্রশিক্ষণের জন্য ব্যবহার করতে পারি? এটি ঠিক যে প্রশিক্ষণ সেটটি কী তা পরিষ্কার নয়।
গিললেভি

16
@ কার্লফিলিপ: আমার পরামর্শ: "ওপেন সোর্স" প্রয়োজনীয়তাটি বাদ দিন। আপনি কোন ভাষা / কাঠামো ব্যবহার করেন তা সত্যিই গুরুত্বপূর্ণ নয়। চিত্র-প্রক্রিয়াকরণ / কম্পিউটার-দৃষ্টি অ্যালগোরিদমগুলি ভাষা অজ্ঞান সম্পর্কিত, সুতরাং আপনি যদি এটি ম্যাটল্যাবে লিখতে পারেন তবে আপনি অবশ্যই এটি ওপেনসিভি বা আপনার পছন্দ মতো অন্য কোনও কাঠামোতে করতে পারেন ... এছাড়াও আপনি এখনও প্রশিক্ষণ / পরীক্ষার চিত্রগুলি কী বিবেচনা করেন তা আমি পরিষ্কার করছি না !
আম্রো

2
আপনার এই 'অনুসন্ধানে' অবদান রাখতে আমাদের সকলকে একত্রিত করার জন্য @ কারলফিলিপ থানক্স! উত্পাদনশীলভাবে কয়েক ঘন্টা ব্যয় করার জন্য এটি একটি দুর্দান্ত সুযোগ ছিল, তবে সবচেয়ে গুরুত্বপূর্ণ, একক সমস্যার জন্য কতগুলি ভিন্ন ভিন্ন পদ্ধতির সন্ধান পাওয়া যায় তা দেখার জন্য ... আশা করি আপনি এটি 1 লা জানুয়ারির জন্য আবার করবেন (সম্ভবত 'একটি দীর্ঘস্থায়ী " সান্তা ক্লজের 'চ্যালেঞ্জ? ;-))
সেপডেক

2
ঠিক আছে, আমি প্রতিযোগিতার উপাদানগুলি অপসারণ করতে প্রশ্নটির উচ্চারণ করেছি। আমি মনে করি এটি এটিকে তার নিজের উপর দাঁড় করানো উচিত just
ব্র্যাড লারসন

উত্তর:


184

আমার কাছে একটি দৃষ্টিভঙ্গি রয়েছে যা আমি মনে করি আকর্ষণীয় এবং বাকী অংশ থেকে কিছুটা আলাদা। অন্যদের কিছু তুলনায় আমার পদ্ধতির মধ্যে মূল পার্থক্য, কিভাবে ইমেজ সেগমেন্টেশন পদক্ষেপ সঞ্চালিত হয় হয় - আমি ব্যবহার DBSCAN পাইথন এর থেকে ক্লাস্টারিং অ্যালগরিদম scikit-শেখো; এটি কিছুটা নিরাকার আকারের সন্ধানের জন্য অনুকূলিত হয়েছে যা অগত্যা কোনও একক পরিষ্কার সেন্ট্রয়েড নাও থাকতে পারে।

শীর্ষ স্তরে, আমার পদ্ধতি মোটামুটি সহজ এবং প্রায় 3 ধাপে বিভক্ত হতে পারে। প্রথমে আমি একটি থ্রেশহোল্ড প্রয়োগ করি (বা বাস্তবে, দুটি পৃথক এবং স্বতন্ত্র প্রান্তিকের যৌক্তিক "বা")। অন্যান্য অনেক উত্তরের মতই, আমি ধরে নিয়েছিলাম যে ক্রিসমাস ট্রি এই দৃশ্যের একটি উজ্জ্বল বস্তুগুলির মধ্যে একটি হবে, সুতরাং প্রথম প্রান্তিকতা কেবল একটি সাধারণ একরঙার উজ্জ্বলতা পরীক্ষা; 0205 স্কেল (যেখানে কালো 0 এবং সাদা 255 হয়) 220 এর উপরে মান সহ যে কোনও পিক্সেল বাইনারি কালো-সাদা ছবিতে সংরক্ষণ করা হয়। দ্বিতীয় প্রান্তে ছয় চিত্রের উপরের বাম এবং নীচের ডানদিকে গাছগুলিতে বিশেষভাবে প্রজ্জ্বলিত লাল এবং হলুদ আলো দেখার চেষ্টা করেছে এবং বেশিরভাগ ফটোতে প্রচলিত নীল-সবুজ পটভূমির বিরুদ্ধে ভালভাবে দাঁড়িয়ে আছে। আমি আরজিবি চিত্রটি এইচএসভি স্পেসে রূপান্তর করি, এবং প্রয়োজন হয় যে বর্ণটি ০.০-১.০ স্কেলে (হলুদ এবং সবুজ রঙের সীমানার সাথে মোটামুটি) বা ০.৯৯ এর চেয়ে বেশি (বেগুনি এবং লাল রঙের সীমান্তের সাথে মিলিত) প্রয়োজন এবং এর জন্য আমার উজ্জ্বল, স্যাচুরেটেড রঙের প্রয়োজন: স্যাচুরেশন এবং মান উভয়ই 0.7 এর উপরে হওয়া উচিত। দুটি প্রান্তিক পদ্ধতির ফলাফলগুলি যৌক্তিকভাবে "বা" একসাথে তৈরি হয় এবং কালো এবং সাদা বাইনারি চিত্রগুলির ফলাফল ম্যাট্রিক্স নীচে প্রদর্শিত হয়:

ক্রিসমাস ট্রি, এইচএসভিতে প্রান্তিককরণের পাশাপাশি একরঙা উজ্জ্বলতার পরে

আপনি পরিষ্কারভাবে দেখতে পাচ্ছেন যে প্রতিটি চিত্রের প্রতিটি গাছের অবস্থানের সাথে মোটামুটিভাবে পিক্সেলের একটি বৃহত ক্লাস্টার রয়েছে, পাশাপাশি কয়েকটি কয়েকটি ইমেজের কয়েকটি উইন্ডোর উইন্ডোতে লাইটের সাথে সামঞ্জস্যযুক্ত কিছু ছোট ছোট ক্লাস্টারও রয়েছে বা একটি দিগন্তের পটভূমি দৃশ্য। পরবর্তী পদক্ষেপটি হ'ল কম্পিউটারটি স্বতন্ত্র ক্লাস্টারগুলি তা সনাক্ত করতে এবং ক্লাস্টার সদস্যতার আইডি নম্বর সহ প্রতিটি পিক্সেল সঠিকভাবে লেবেল করা।

এই কাজের জন্য আমি ডিবিএসসিএএন নির্বাচন করেছি । একটি প্রশংসনীয় ভাল চাক্ষুষ তুলনা নেই কিভাবে DBSCAN সাধারণত আচরণ করবে, অন্যান্য ক্লাস্টারিং আলগোরিদিম আপেক্ষিক, উপলব্ধ এখানে । যেমনটি আমি আগেই বলেছি, এটি নিরাকার আকারগুলির সাথে ভাল করে। প্রতিটি ক্লাস্টার আলাদা রঙে চক্রান্ত করে ডিবিএসসিএএন-এর আউটপুট এখানে প্রদর্শিত হয়:

ডিবিএসসিএন ক্লাস্টারিং আউটপুট

এই ফলাফলটি দেখার সময় কয়েকটি বিষয় সচেতন হতে হবে। প্রথমটি হ'ল ডিবিএসসিএএন এর আচরণ নিয়ন্ত্রণের জন্য একটি "প্রক্সিমিটি" প্যারামিটার নির্ধারণ করতে পারে, যা কার্যকরভাবে নিয়ন্ত্রণ করে যে অ্যালগোরিদমকে পরীক্ষার পয়েন্টকে একত্রিত করার পরিবর্তে একটি নতুন পৃথক ক্লাস্টার ঘোষণার জন্য কীভাবে পৃথক করা উচিত a ইতিমধ্যে একটি পূর্ব-বিদ্যমান ক্লাস্টার। আমি প্রতিটি চিত্রের তির্যক বরাবর এই মানটি 0.04 গুণ আকারে সেট করেছি। যেহেতু চিত্রগুলি মোটামুটি ভিজিএ থেকে প্রায় এইচডি 1080 পর্যন্ত আকারে পরিবর্তিত হয়, এই জাতীয় স্কেল-আপেক্ষিক সংজ্ঞাটি সমালোচনাযোগ্য।

আরেকটি বিষয় লক্ষণীয় যে ডিবিএসসিএএন অ্যালগরিদম এটি বিজ্ঞান-শিখায় প্রয়োগ করা হয়েছে মেমরির সীমাবদ্ধতা রয়েছে যা এই নমুনায় কিছু বৃহত্তর চিত্রের জন্য মোটামুটি চ্যালেঞ্জিং। অতএব, কয়েকটি বৃহত্তর চিত্রের জন্য, এই সীমাতে থাকার জন্য আমাকে প্রতিটি ক্লাস্টারটি আসলে "ডেসিমেট" করতে হয়েছিল (অর্থাত্ কেবল প্রতি তৃতীয় বা চতুর্থ পিক্সেল ধরে রাখতে হবে এবং অন্যগুলি ফেলে দিতে হবে)। এই ক্লুলিং প্রক্রিয়াটির ফলাফল হিসাবে, পৃথক পৃথক স্পার্স পিক্সেলগুলি বৃহত্তর কিছু চিত্র দেখতে অসুবিধা হয়। সুতরাং, কেবল প্রদর্শনের উদ্দেশ্যে, উপরের চিত্রগুলিতে বর্ণ-কোড কোডযুক্ত পিক্সেলগুলি কার্যকরভাবে কিছুটা "dilated" হয়েছে যাতে তারা আরও ভালভাবে দাঁড়ায়। এটি আখ্যানটির স্বার্থে একটি প্রসাধনী অপারেশন; যদিও এই কোডটি আমার কোডটিতে উল্লেখ করার মত মন্তব্য রয়েছে,

একবার ক্লাস্টারগুলি চিহ্নিত করা এবং লেবেল করা হয়ে গেলে তৃতীয় এবং চূড়ান্ত পদক্ষেপটি সহজ: আমি প্রতিটি চিত্রের মধ্যে বৃহত্তম ক্লাস্টারটি গ্রহণ করি (এই ক্ষেত্রে, আমি সদস্য পিক্সেলের মোট সংখ্যার দিক থেকে "আকার" পরিমাপ করতে পছন্দ করি, যদিও কেউ পারে পরিবর্তে সহজেই পরিবর্তে এমন কোনও ধরণের মেট্রিক ব্যবহার করা হয়েছে যা শারীরিক পরিমাপের গতি ঘটিয়েছে) এবং সেই ক্লাস্টারের জন্য উত্তল হলের গণনা করুন। উত্তল হলটি গাছের সীমানায় পরিণত হয়। এই পদ্ধতির মাধ্যমে গণনা করা ছয় উত্তল হাল নীচে লাল দেখানো হয়েছে:

ক্রিসমাস ট্রি তাদের গণনা করা সীমানা সহ

সোর্স কোড পাইথন 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()

@ লেনোন 310 এর সমাধান ক্লাস্টারিং। (কে-অর্থ)
ব্যবহারকারী 3054997

1
@ স্ট্যাচাইরা আমার সহজ পদ্ধতির প্রস্তাব দেওয়ার আগে আমি এই পদ্ধতির কথাও ভেবেছিলাম। আমি মনে করি এটির অন্যান্য ক্ষেত্রেও ভাল ফলাফলের প্রসার ও সাধারণীকরণের দুর্দান্ত সম্ভাবনা রয়েছে। ক্লাস্টারিংয়ের জন্য আপনি নিউরাল নেট নিয়ে পরীক্ষা করতে পারেন। কোনও এসওএম বা নিউরাল গ্যাসের মতো কিছু দুর্দান্ত কাজ করবে। তবুও, দুর্দান্ত প্রস্তাব এবং আমার কাছ থেকে থাম্বস আপ!
sepdek

4
@ ফাস্ট অ্যান্ড রায়ান কার্লসন: ধন্যবাদ, ছেলেরা! হ্যাঁ, আমি সম্মত হচ্ছি যে আপভোট সিস্টেমটি যখন একে অপরের কয়েক ঘন্টার মধ্যে জমা দেওয়া 2 বা 3 সংক্ষিপ্ত উত্তরগুলির মধ্যে বিচার করার পক্ষে ভাল কাজ করে তবে দীর্ঘ উত্তরগুলির সাথে প্রতিযোগিতার ক্ষেত্রে যখন বর্ধিত সময়কালের বাইরে চলে যায় এর গুরুতর পক্ষপাতিত্ব রয়েছে agree । এক কিছুর জন্য, পরবর্তী সাবমিশনগুলি পরে জনসাধারণের পর্যালোচনার জন্য উপলব্ধ হওয়ার আগেই উর্ধ্বতনগুলি জমা হওয়া শুরু করে। এবং যদি উত্তরগুলি সমস্ত দীর্ঘ হয়, তবে যেহেতু কেউ একটি নিয়মিত সীসা প্রতিষ্ঠা করেন, প্রায়শই একটি "ব্যান্ডওয়্যাগন ইফেক্ট" দেখা দেয় কারণ লোকেরা বাকী পড়াটি বিরক্ত না করে কেবল প্রথমটিকে উপস্থাপন করে।
stachyra

2
@ স্টাচির দুর্দান্ত খবর বন্ধু! উষ্ণতম অভিনন্দন এবং এটি আপনার নতুন বছরের জন্য একটি সূচনা হতে পারে!
সেপডেক

1
@ lennon310: আমি এই সমস্যা এখনো একটি স্থানীয় সর্বাধিক সনাক্তকরণ ফিল্টার চেষ্টা করিনি, কিন্তু যদি আপনি এটি নিজের অন্বেষণ করতে চান, scipy অন্তর্ভুক্ত এই এক । এই প্রকল্পের জন্য আমার পাইথনের উত্স কোডটি এত ছোট ছিল যে আমি এটির 100% প্রকাশ করতে পেরেছি; আক্ষরিকভাবে আপনাকে যা করতে হবে তা হ'ল আমার দুটি কোড স্নিপেটগুলি আলাদা .py ফাইলগুলিতে অনুলিপি করে আটকান এবং তারপরে scipy.ndimage.filters.maximum_filter()যেখানে আমি একটি প্রান্তিকতা ব্যবহার করেছি সেখানে একই জায়গায় কল কল করে itute
stachyra

145

সম্পাদনা দ্রষ্টব্য: আমি প্রয়োজনীয়তা অনুসারে অনুরোধ অনুসারে প্রতিটি গাছের চিত্র পৃথকভাবে প্রক্রিয়া করতে এই পোস্টটি সম্পাদনা করেছি, (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();
}
}

দ্বিতীয় ধাপে, আকার গঠনের জন্য চিত্রটির সবচেয়ে উজ্জ্বল পয়েন্টগুলি dilated হয়। এই প্রক্রিয়াটির ফলাফলটি উল্লেখযোগ্য উজ্জ্বলতার সাথে অবজেক্টগুলির সম্ভাব্য আকার। বন্যার ফিল সেগমেন্টেশন প্রয়োগ করে, সংযোগ বিচ্ছিন্ন আকারগুলি সনাক্ত করা হয়।

আউটপুট চিত্রগুলি:

সোর্স কোড:

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();
}
}

এই পদ্ধতির সুবিধাটি হ'ল সত্যটি এটি সম্ভবত অন্যান্য আলোকিত বস্তুযুক্ত চিত্রগুলির সাথে কাজ করবে যেহেতু এটি বস্তুর আকৃতিটি বিশ্লেষণ করে।

মেরি ক্রিসমাস!


সম্পাদনা নোট 2

এই সমাধানটির আউটপুট চিত্রগুলির মিল এবং অন্য কিছুগুলির বিষয়ে আলোচনা রয়েছে। আসলে, তারা খুব অনুরূপ। তবে এই পদ্ধতিটি কেবলমাত্র বস্তুগুলিকে বিভাগ করে না। এটি কিছু অর্থে বস্তুর আকারগুলিও বিশ্লেষণ করে। এটি একই দৃশ্যে একাধিক আলোকিত বস্তু পরিচালনা করতে পারে। আসলে ক্রিসমাস ট্রি সবচেয়ে উজ্জ্বল হওয়া দরকার নেই। আমি আলোচনাটি সমৃদ্ধ করার জন্য এটি কেবল আবৃত করছি। নমুনাগুলিতে একটি পক্ষপাত আছে যা কেবলমাত্র উজ্জ্বল বস্তুর সন্ধানে আপনি গাছগুলি দেখতে পাবেন। তবে, আমরা কি এই মুহুর্তে আলোচনাটি থামাতে চাই? এই মুহুর্তে, কম্পিউটার সত্যিই কতক্ষণ একটি ক্রিসমাস ট্রি সদৃশ একটি বস্তুকে স্বীকৃতি দিচ্ছে? এই ফাঁক বন্ধ করার চেষ্টা করা যাক।

নীচে কেবল এই পয়েন্টটি ব্যাখ্যা করার জন্য একটি ফলাফল উপস্থাপন করা হয়েছে:

ইনপুট চিত্র

এখানে চিত্র বর্ণনা লিখুন

আউটপুট

এখানে চিত্র বর্ণনা লিখুন


2
এটা আকর্ষণীয়. আমি আশা করি প্রতিটি চিত্র পৃথকভাবে প্রক্রিয়া করার সময় আপনি একই ফলাফল পেতে পারেন। আমি 4 ঘন্টা আগে প্রশ্নটি সম্পাদনা করেছিলাম আপনার উত্তরটি বিশেষভাবে উল্লেখ করার জন্য পোস্ট করেছিলাম। আপনি যদি এই ফলাফলগুলির সাথে আপনার উত্তর আপডেট করতে পারেন তবে দুর্দান্ত লাগবে।
করলফিলিপ

@ মার্ভিন আপনার ত্রিভুজ সনাক্তকরণে, আপনি ভরগুলির ওঠানামা কীভাবে পরিচালনা করেছিলেন? এটি কোনও কঠোর ত্রিভুজ নয়, ভর পরিবর্তিত হিসাবে ভর
একক

2
@ ব্যবহারকারী 3054997: এটি অন্য একটি বিষয়। আমি পোস্ট হিসাবে, অ্যালগরিদম কঠোর ত্রিভুজ আকার জন্য সন্ধান করে না। এটি প্রতিটি বস্তুর বিশ্লেষণ করে এবং এমন একটি গাছকে বিবেচনা করে যা একটি সাধারণ মাপদণ্ডের সাথে একটি ত্রিভুজকে "সাদৃশ্যযুক্ত" করে তোলে: বস্তুর ভর y বৃদ্ধি হিসাবে বৃদ্ধি পেতে ব্যবহৃত হয় এবং প্রতিটি অনুভূমিক বস্তুর অংশের কেন্দ্র প্রায় একে অপরের সাথে কেন্দ্রীভূত হয় ।
গ্যাব্রিয়েল অম্ব্রেসিও আরচানজো

@ মারভিন আমার সমাধানটি খুব সহজ, আমি আমার উত্তরেও এটি জানিয়েছি। যাইহোক এটি আপনার প্রথম সমাধানের চেয়ে ভাল কাজ করেছে। যদি আমি সঠিকভাবে স্মরণ করি তবে আপনার প্রথম উত্তরে আপনি ছোট আলোর টেক্সচার সনাক্ত করতে বৈশিষ্ট্য বর্ণনাকারীর কথা বলেছেন, যা আপনি এখানে করছেন তা নয়। আমি কেবল বলেছিলাম যে আপনার বর্তমান পদ্ধতির এবং ফলাফলগুলি আপনার প্রথম সমাধানের চেয়ে আমার সাথে অনেক বেশি মিল। অবশ্যই আপনি এটি স্বীকার করবেন বলে আমি আশা করি না, আমি এটি কেবল রেকর্ডের জন্য বলেছি।
স্মোস

1
@ সেপডেক এখানে এমন বেশ কয়েকটি সমাধান রয়েছে যা আমার চেয়ে সত্যই ভাল এবং তারা এখনও আমার উর্ধ্বে অর্ধেক পাচ্ছে getting অন্যান্য সমাধানগুলি দ্বারা "অনুপ্রাণিত হওয়ার" ক্ষেত্রে কোনও ভুল নেই। আমি আপনার সমাধানগুলিও দেখেছি, আপনার বিরুদ্ধে আমার বলার মতো কিছুই নেই, আপনি সেগুলি আমার পরে পোস্ট করেছিলেন এবং আমার "ধারণা" বলতে এতটা আসল ছিল না যে আপনি কেবল আমাকে অনুলিপি করেছেন। তবে মারভিন একমাত্র তিনিই ছিলেন যিনি আমার আগে পোস্ট করেছিলেন এবং সম্পাদনাটি হ'ল সমাধানটি একই অ্যালগরিদম ব্যবহার করে দেখার পরে ... কমপক্ষে তিনি বলতে পারতেন "হ্যাঁ, আমি আপনার সমাধানটি পছন্দ করেছি এবং আমি এটি পুনরায় ব্যবহার করেছি" কোনও ভুল নেই, এটি ঠিক একটি খেলা.
স্মোস

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 টি ইনপুট চিত্র রয়েছে এবং উত্তরটিতে তাদের প্রতিটি প্রসেসিংয়ের ফলাফল প্রদর্শন করা উচিত;
করলফিলিপ

ওহে! তুমি আমার প্রোগ্রাম CLI আর্গুমেন্ট হিসাবে ফাইলের নামের পাস করতে পারেন: ./christmas_tree ./*.png। এগুলি আপনি চান যতগুলি হতে পারে, ফলাফলগুলি অন্যের পরে কোনও কী চাপলে একের পরে প্রদর্শিত হবে। এটা কি ভুল?
স্মেসো

এটি ঠিক আছে, তবে আপনাকে এখনও ছবিগুলি আপলোড করতে হবে এবং সেগুলি আপনার প্রশ্নে ভাগ করতে হবে যাতে থ্রেডের দর্শকরা আসলে আপনার ফলাফল দেখতে পারে। লোকেরা কী করেছে তা দেখতে দেওয়া আপনার ভোট পাওয়ার সম্ভাবনার উন্নতি করবে;)
করলফিলিপ

আমি এটির জন্য একটি সমাধান অনুসন্ধান করার চেষ্টা করছি, আমার কিছু সংযোগের সমস্যা আছে।
স্মোস

2
গ্রেট! এখন আপনি নীচের কোড দিয়ে উত্তরের ভিতরে এগুলি পুনরুদ্ধার করতে পারেন: <img src="http://i.stack.imgur.com/nmzwj.png" width="210" height="150">কেবল ছবির লিঙ্কটি পরিবর্তন করুন;)
করলফিলিপ

60

আমি মতলব আর -2007 এ কোডটি লিখেছি। আমি ক্রিসমাস ট্রি আনুমানিকভাবে নিষ্কাশন করতে কে-অর্থ ব্যবহার করেছি used আমি কেবলমাত্র একটি চিত্রের সাথে আমার মধ্যবর্তী ফলাফল এবং সমস্ত ছয়টির সাথে চূড়ান্ত ফলাফল দেখাব।

প্রথমত, আমি আরজিবি স্থানটি ল্যাব স্পেসে ম্যাপ করেছি, যা এর বি চ্যানেলে লাল রঙের বৈপরীত্যকে বাড়িয়ে তুলতে পারে:

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 টি বৈশিষ্ট্য রয়েছে তাই আমি আমার ক্লাস্টারিং পদ্ধতিতে কে = 5 বেছে নেব। কে-মানেগুলির কোডটি নীচে দেখানো হয়েছে (এটি ডাঃ অ্যান্ড্রু এনজি'র মেশিন লার্নিং কোর্স থেকে।

[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) সেন্ট্রয়েডগুলিতে আর কোনও পরিবর্তন করা যায় না। আমার পরীক্ষার জন্য, পুনরাবৃত্তির পরিমাণ বাড়ানো পটভূমির (আকাশ এবং গাছ, আকাশ এবং বিল্ডিং, ...) আরও নির্ভুলভাবে পার্থক্য করতে পারে তবে ক্রিসমাস ট্রি নিষ্কাশনের ক্ষেত্রে কোনও কঠোর পরিবর্তন দেখায়নি। এছাড়াও মনে রাখবেন কে-ই মানেগুলি এলোমেলো সেন্ট্রয়েড সূচনাতে প্রতিরোধক নয়, তাই তুলনা করতে বেশ কয়েকবার প্রোগ্রাম চালানোর পরামর্শ দেওয়া হয়।

কে-ইনের পরে, সর্বাধিক তীব্রতার সাথে লেবেলযুক্ত অঞ্চলটি I0বেছে নেওয়া হয়েছিল। এবং সীমানা উত্তোলনের জন্য সীমানা ট্রেসিং ব্যবহার করা হত। আমার কাছে, শেষ ক্রিসমাস ট্রিটি উত্তোলন করা সবচেয়ে কঠিন কারণ যে চিত্রটির বৈপরীত্যটি প্রথম পাঁচটিতে রয়েছে তেমন উচ্চতর নয়। আমার পদ্ধতির আরেকটি বিষয় হ'ল আমি bwboundariesসীমানাটি সনাক্ত করতে মতলবতে ফাংশন ব্যবহার করেছি , তবে কখনও কখনও অভ্যন্তরীণ সীমানাগুলিও অন্তর্ভুক্ত থাকে কারণ আপনি তৃতীয়, ৫ ম, 6th ষ্ঠ ফলাফলতে পর্যবেক্ষণ করতে পারেন। ক্রিসমাস গাছগুলির মধ্যে অন্ধকার দিক কেবল আলোকিত দিকের সাথেই ক্লাস্টার হতে ব্যর্থ হয় না, তবে তারা এতগুলি ক্ষুদ্র অভ্যন্তরীণ সীমানাও সন্ধান করে ( imfillখুব বেশি উন্নতি করে না)। আমার সমস্ত অ্যালগরিদমে এখনও অনেক উন্নতির স্থান রয়েছে।

কিছু প্রকাশনার ইঙ্গিত দেয় যে মিড-শিফট কে-মানেগুলির চেয়ে বেশি শক্তিশালী হতে পারে এবং অনেকগুলি গ্রাফ-কাট ভিত্তিক অ্যালগরিদমগুলি জটিল সীমানা বিভাজনে খুব প্রতিযোগিতামূলক। আমি নিজেই একটি গড়-শিফট অ্যালগরিদম লিখেছিলাম, মনে হয় যথেষ্ট পরিমাণ আলো না থাকলে অঞ্চলগুলি আরও ভালভাবে বের করা যায়। তবে গড়-শিফটটি কিছুটা ওভার-সেগমেন্টেড এবং মার্জ করার কিছু কৌশল প্রয়োজন। এটি আমার কম্পিউটারে কে-অর্থের চেয়েও ধীর গতিতে চলেছে, আমি ভয় করি যে আমাকে এটি ছেড়ে দিতে হবে। উপরে উত্সাহিত সেই আধুনিক অ্যালগরিদমগুলির সাথে অন্যরা এখানে দুর্দান্ত ফলাফল জমা দেবে তা দেখার জন্য আমি অধীর আগ্রহে অপেক্ষা করছি।

তবুও আমি সর্বদা বিশ্বাস করি যে বৈশিষ্ট্য নির্বাচনটি চিত্র বিভাজনের মূল উপাদান। একটি উপযুক্ত বৈশিষ্ট্য নির্বাচন যা অবজেক্ট এবং পটভূমির মধ্যে মার্জিনকে সর্বাধিক করে তুলতে পারে, অনেকগুলি বিভাজন আলগোরিদিম অবশ্যই কাজ করবে। বিভিন্ন অ্যালগরিদমগুলি ফলাফলটি 1 থেকে 10 পর্যন্ত উন্নত করতে পারে তবে বৈশিষ্ট্য নির্বাচন এটি 0 থেকে 1 পর্যন্ত উন্নতি করতে পারে।

মেরি ক্রিসমাস!


2
উত্তরের জন্য ধন্যবাদ! আমি কেবল উল্লেখ করতে চেয়েছিলাম যে মতলব ওপেন সোর্স নয় , তবে সায়্লাব । আমিও এই উত্তরটি অন্যদের সাথে প্রতিযোগিতা করে দেখতে পছন্দ করব। ;)
করলফিলিপ

6
আপনাকে ধন্যবাদ কার্ল অক্টাভ আরেকটি ওপেন সোর্স সফ্টওয়্যার যা মতলব: mathworks.fr/matlabcentral/answers/14399-gnu-octave-vs-matlab এর সাথে প্রায় একই কোডিং ব্যাকরণ ভাগ করে দেয়
lennon310

মজার, আমি জানতাম না, ধন্যবাদ! আপনার কোডটি কি অষ্টাভে কাজ করে?
করলফিলিপ

আমি এখনও পরীক্ষা করিনি, তবে আমি মনে করি এটি কোনও সমস্যা নয় :)
lennon310

@ লেনন ৩১০ আমি মনে করি আপনি যদি গণ্ডিগুলি ফেলে দেন এবং উত্তল হালটি পান তবে আপনি গর্তের সমস্যা থেকে মুক্তি পাবেন। মনে রাখবেন উত্তল হাল হ'ল ক্ষুদ্রতম অঞ্চল যা একটি সেটে সমস্ত পয়েন্ট অন্তর্ভুক্ত করে।
sepdek

57

Myতিহ্যগত চিত্র প্রক্রিয়াকরণ পদ্ধতির সাহায্যে এটি আমার চূড়ান্ত পোস্ট ...

এখানে আমি আরও দু'টি প্রস্তাব একত্রিত করে আরও ভাল ফলাফল অর্জন করেছি । প্রকৃতপক্ষে আমি দেখতে পাচ্ছি না কীভাবে এই ফলাফলগুলি আরও ভাল হতে পারে (বিশেষত আপনি যখন পদ্ধতিটি তৈরির মুখোশযুক্ত চিত্রগুলি দেখেন)।

পদ্ধতির কেন্দ্রে তিনটি মূল অনুমানের সংমিশ্রণ :

  1. চিত্রগুলিতে গাছের অঞ্চলে উচ্চ ওঠানামা হওয়া উচিত
  2. গাছের অঞ্চলে চিত্রগুলির উচ্চতর তীব্রতা থাকতে হবে
  3. পটভূমি অঞ্চলগুলির তীব্রতা কম হওয়া উচিত এবং বেশিরভাগই নীল-ইশ হওয়া উচিত

এই অনুমানগুলি মাথায় রেখে পদ্ধতিটি নিম্নলিখিতভাবে কাজ করে:

  1. চিত্রগুলি এইচএসভিতে রূপান্তর করুন
  2. একটি লোজি ফিল্টার সহ ভি চ্যানেল ফিল্টার করুন
  3. 'ক্রিয়াকলাপ' মাস্ক এ পাওয়ার জন্য এলওজি ফিল্টার করা ইমেজে হার্ড থ্রোসোল্ডিং প্রয়োগ করুন
  4. তীব্রতা মাস্ক বি পেতে হার্ড চ্যানেলটিতে হার্ড থ্রোসোল্ডিং প্রয়োগ করুন
  5. কম তীব্রতা নীল-ইশ অঞ্চলগুলিকে ব্যাকগ্রাউন্ড মাস্ক সিতে ক্যাপচার করতে এইচ চ্যানেল প্রান্তিককরণ প্রয়োগ করুন
  6. চূড়ান্ত মাস্কটি পেতে এবং ব্যবহার করে মাস্কগুলি একত্রিত করুন
  7. অঞ্চলগুলি প্রসারিত করতে এবং ছড়িয়ে ছিটিয়ে থাকা পিক্সেলগুলি সংযুক্ত করতে মাস্কটি বিভক্ত করুন
  8. ছোট অঞ্চলগুলি নির্মূল করুন এবং চূড়ান্ত মুখোশ পান যা শেষ পর্যন্ত কেবল গাছকে উপস্থাপন করবে

এমএটিএলবিএজে কোডটি এখানে (আবার, স্ক্রিপ্টটি বর্তমান ফোল্ডারে সমস্ত জেপিজি চিত্র লোড করে এবং আবার এটি কোডের একটি অনুকূলিত অংশ হতে দূরে থাকে):

% 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 হয় এবং তারা অনুরূপ বাক্য গঠন এবং ফাংশন সরবরাহ করে। ;)
করলফিলিপ


@ কারলফিলিপ কোনওভাবেই এই প্রশ্নটির একটি মাতলাব ট্যাগ থাকার শেষ হয়েছে। ওপেন সোর্স যদি সত্যিই প্রয়োজন হয় তবে আমি এটিকে অপসারণের পরামর্শ দেব।
ডেনিস জাহেরউদ্দিন

@ সেপডেক খুব সুন্দর, সম্ভবত চূড়ান্ত ছবিতে 'গর্ত' অন্তর্ভুক্ত করার জন্য কিছু করা যেতে পারে। (অনুমোদিত পিক্সেল দ্বারা পরিবেষ্টিত সমস্ত পিক্সেল যুক্ত করুন ?!)
ডেনিস জাহেরউদ্দিন

1
@ কার্লফিলিপ থেক্স ম্যান! আপনি আমার পদ্ধতির আকর্ষণীয় পেয়েছিলেন বলে আমি আনন্দিত। এছাড়াও আমি আপনাকে সবচেয়ে মার্জিত সমাধানটি বেছে নেওয়ার জন্য অভিনন্দন জানাতে চাই এবং সবচেয়ে বেশি ভোট দিয়ে একটিও নয় !!!
সেপডেক

36

আমার সমাধানের পদক্ষেপগুলি:

  1. আর চ্যানেল পান (আরজিবি থেকে) - এই চ্যানেলে আমরা যে সমস্ত ক্রিয়াকলাপ তৈরি করি:

  2. আগ্রহের অঞ্চল তৈরি করুন (আরওআই)

    • ন্যূনতম মান 149 (শীর্ষে ডান চিত্র) সহ থ্রেশহোল্ড আর চ্যানেল

    • দ্বৈত ফলাফলের অঞ্চল (মাঝের বাম চিত্র)

  3. গণিত রুইতে যুগ সনাক্ত করুন। গাছের অনেকগুলি কিনারা রয়েছে (মাঝের ডান চিত্র)

    • ফলত ফল

    • বড় ব্যাসার্ধের সাথে ইরোড (নীচে বাম চিত্র)

  4. বৃহত্তম (অঞ্চল অনুযায়ী) অবজেক্টটি নির্বাচন করুন - এটি ফলাফলের অঞ্চল

  5. উত্তলহুল (গাছ উত্তল বহুভুজ) (নীচের ডান চিত্র)

  6. বাউন্ডিং বাক্স (নীচের ডান চিত্র - গ্রান বক্স)

ধাপে ধাপে: এখানে চিত্র বর্ণনা লিখুন

প্রথম ফলাফল - সর্বাধিক সহজ তবে ওপেন সোর্স সফ্টওয়্যারটিতে নয় - "অ্যাডাপটিভ ভিশন স্টুডিও + অ্যাডাপটিভ ভিশন লাইব্রেরি": এটি ওপেন সোর্স নয় তবে প্রোটোটাইপের পক্ষে খুব দ্রুত:

ক্রিসমাস ট্রি সনাক্ত করতে পুরো অ্যালগরিদম (১১ টি ব্লক): AVL সমাধান

পরবর্তী পর্ব. আমরা ওপেন সোর্স সমাধান চাই। ওপেনসিভি ফিল্টারগুলিতে এভিএল ফিল্টারগুলি পরিবর্তন করুন: এখানে আমি সামান্য পরিবর্তন করেছি যেমন এজ ডিটেকশন সিভিকেনি ফিল্টার ব্যবহার করে, রাইয়ের প্রতি শ্রদ্ধা জানাতে আমি প্রান্তের চিত্রের সাথে বহুগুণ অঞ্চল চিত্রটি ব্যবহার করেছি, আমি সর্বাধিক উপাদানটি সন্ধান করেছি ফাইন্ডকন্টস + কনট্যুরআরিয়া তবে ধারণাটি একই।

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

ওপেনসিভি সমাধান

আমি এখন মধ্যবর্তী পদক্ষেপের সাথে চিত্রগুলি দেখাতে পারছি না কারণ আমি কেবল 2 টি লিঙ্ক রাখতে পারি।

ঠিক আছে এখন আমরা ওপেনসোর্স ফিল্টার ব্যবহার করি তবে এটি এখনও পুরো ওপেন সোর্স নয়। শেষ পদক্ষেপ - সি ++ কোডে পোর্ট। আমি ভার্সন ২.৪.৪ এ ওপেনসিভি ব্যবহার করেছি

চূড়ান্ত সি ++ কোডের ফলাফল: এখানে চিত্র বর্ণনা লিখুন

সি ++ কোডটিও খুব ছোট:

#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 ব্যবহার করেছি। আপনার সমর্থন সি ++ 11 সহ সি ++ সংকলক ব্যবহার করা উচিত।
অ্যাডামএফ

আমার এটির সাথে আমার কোনও সিস্টেম নেই। আপনি কি std::max_element()কলটি আবার লিখতে পারবেন? আমি আপনার উত্তর পুরষ্কার চাই। আমার মনে হয় আমার জিসিসি ৪.২ আছে।
করলফিলিপ

ঠিক আছে এটি সি ++ 11 বৈশিষ্ট্য;) আমি উপরের উত্স কোডটি পরিবর্তন করেছি। দয়া করে এখন চেষ্টা করুন।
অ্যাডামএফ

ঠিক আছে ধন্যবাদ. আমি এটি পরীক্ষা করেছি এবং এটি সুন্দর। এই প্রশ্নটি আবার খোলার সাথে সাথেই (অন্যান্য ব্যবহারকারীরা আমাকে এটিতে সহায়তা করতে হবে) আমি আপনাকে পুরষ্কারের জন্য আরও একটি অনুগ্রহ নির্ধারণ করতে পারি। অভিনন্দন!
করলফিলিপ

31

... আরেকটি পুরানো ধরণের সমাধান - সম্পূর্ণরূপে এইচএসভি প্রক্রিয়াকরণের উপর ভিত্তি করে :

  1. চিত্রগুলিকে এইচএসভি রঙের জায়গাতে রূপান্তর করুন
  2. এইচএসভিতে হিউরিস্টিক্স অনুযায়ী মাস্ক তৈরি করুন (নীচে দেখুন)
  3. সংযোগ বিচ্ছিন্ন অঞ্চলগুলিতে সংযোগ স্থাপনের জন্য মুখোশগুলিতে রূপচর্চা বিশিষ্টতা প্রয়োগ করুন
  4. ছোট অঞ্চল এবং অনুভূমিক ব্লকগুলি বাতিল করুন (মনে রাখবেন গাছগুলি উল্লম্ব ব্লকগুলি রয়েছে)
  5. বাউন্ডিং বাক্সটি গণনা করুন

এইচএসভি প্রক্রিয়াকরণে হিউরিস্টিক্স সম্পর্কিত একটি শব্দ :

  1. 210 - 320 ডিগ্রির মধ্যে হিউস (এইচ) এর সাথে সমস্ত কিছু নীল-ম্যাজেন্টা হিসাবে ফেলে দেওয়া হয় যা ব্যাকগ্রাউন্ডে বা অ-প্রাসঙ্গিক অঞ্চলে বলে মনে করা হয়
  2. সাথে সব কিছু মানগুলি (v) কম যে 40% খুব প্রাসঙ্গিক হওয়ার অন্ধকার হচ্ছে বাতিল করা হয়

অবশ্যই কেউ এই পদ্ধতির সূক্ষ্ম-টিউন করার জন্য অসংখ্য অন্যান্য সম্ভাবনা নিয়ে পরীক্ষা করতে পারে ...

কৌশলটি করার জন্য ম্যাটল্যাব কোডটি এখানে রয়েছে (সতর্কতা: কোডটি অপ্টিমাইজড হওয়া অনেক দূরের !!! !!! আমি ম্যাটল্যাব প্রোগ্রামিংয়ের জন্য প্রস্তাবিত কৌশলগুলি ব্যবহার করেছি যাতে প্রক্রিয়াটিতে কোনও কিছু ট্র্যাক করতে সক্ষম হতে পারে - এটি ব্যাপকভাবে অনুকূল করা যেতে পারে):

% 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
@ কার্লফিলিপ সেপডেকের ছবিগুলি ভাগ করে নেওয়ার মতো খ্যাতি নেই, আমি তার লিঙ্ক এবং নির্দেশাবলী অনুসারে ছবিগুলিকে উত্তর বঙ্গে স্থানান্তরিত করেছি। যদিও নিশ্চিত না যে সেগুলি সঠিক, এই অংশে মন্তব্য করতে দ্বিধা বোধ করবেন না।
alko

@ আলকো আমি জানি, ধন্যবাদ তবে আপনি ভাগ করেছেন এমন কয়েকটি চিত্র ইনপুট সেটে ছিল না । উত্তরে অবশ্যই প্রশ্নটিতে ভাগ করা সমস্ত 6 টি চিত্র প্রক্রিয়াকরণের ফলাফলটি প্রদর্শিত হবে।
করলফিলিপ

@ কার্ফিলিপ এটি তার চিত্র নয়, আমার নয়। "বহুলাংশে এই মন্তব্যটি" বলতে আমি বোঝাতে চেয়েছি;)
আলকো

2
সমস্যা সৃষ্টি করার জন্য দুঃখিত ... আমার উদ্দেশ্য নয় আমি প্রাথমিক ডেটাসেটে সমস্ত চিত্র অন্তর্ভুক্ত করেছি এবং এটি আরও প্রমাণ দিয়ে প্রমাণ করেছি যে আমার ধারণাটি
দৃust়

23

কিছু পুরানো ফ্যাশন ইমেজ প্রসেসিংয়ের পদ্ধতির ...
ধারণাটি এই ধারণার উপর ভিত্তি করে তৈরি করা হয় যে চিত্রগুলি সাধারণত গা dark় এবং মসৃণ ব্যাকগ্রাউন্ডে (বা কিছু ক্ষেত্রে অগ্রভাগ) আলোকিত গাছগুলি চিত্রিত করেপ্রতিভাত গাছ এলাকায় আরো "অনলস" এবং উচ্চতর তীব্রতা হয়েছে
প্রক্রিয়াটি নিম্নরূপ:

  1. গ্রেলেভেলে রূপান্তর করুন
  2. সর্বাধিক "সক্রিয়" অঞ্চলগুলি পেতে লোজি ফিল্টারিং প্রয়োগ করুন
  3. সর্বাধিক উজ্জ্বল অঞ্চলগুলি পেতে ইন্টেন্টিসি থ্রোহোল্ডিং প্রয়োগ করুন
  4. প্রারম্ভিক মাস্কটি পেতে পূর্ববর্তী 2 টি একত্রিত করুন
  5. অঞ্চলগুলি প্রসারিত করতে এবং পার্শ্ববর্তী উপাদানগুলিকে সংযুক্ত করতে একটি রূপচর্চা ডিসলেশন প্রয়োগ করুন
  6. তাদের ক্ষেত্রফলের আকার অনুসারে ছোট প্রার্থী অঞ্চলগুলি বাদ দিন

আপনি যা পান তা হ'ল প্রতিটি চিত্রের জন্য বাইনারি মাস্ক এবং একটি সীমানা বাক্স।

এই নিষ্পাপ কৌশলটি ব্যবহার করে ফলাফলগুলি এখানে: এখানে চিত্র বর্ণনা লিখুন

ম্যাটল্যাবে কোডটি অনুসরণ করে: কোডটি জেপিজি চিত্র সহ একটি ফোল্ডারে চলে। সমস্ত চিত্র লোড করে এবং ফলাফল সনাক্ত করে।

% 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

ফাউস্টের মতো ফলস্বরূপ চিত্রগুলি আপলোড করতে ভুলবেন না।
করলফিলিপ

আমি এখানে নুব তাই আমি ছবি আপলোড করতে পারি না। আমার বিবরণে প্রদত্ত লিঙ্কগুলিতে ফলাফল দেখুন।
sepdek

ঠিক আছে, তবে আপনাকে এখনও অন্যের মতো করা প্রশ্নটিতে ভাগ করা চিত্রগুলি ব্যবহার করতে হবে। আপনি সেগুলি প্রক্রিয়া করার পরে, এটি অন্য কোথাও আপলোড করুন এবং লিঙ্কগুলি যুক্ত করতে আপনার উত্তর সম্পাদনা করুন। পরে আমি আপনার উত্তরটি সম্পাদনা করব এবং চিত্রগুলি এটির জন্য রেখে দেব।
করলফিলিপ

লিঙ্কটিতে এখন সঠিক চিত্র রয়েছে বলে মনে হচ্ছে।
ডেনিস জাহেরুদ্দিন

22

আমি যা দেখেছি তার থেকে একেবারে ভিন্ন পদ্ধতির ব্যবহার করে আমি একটি তৈরি করেছি স্ক্রিপ্ট যা তাদের আলোতে ক্রিসমাস গাছ সনাক্ত করে। ফলাফল সর্বদা একটি প্রতিসম ত্রিভুজ নয় এবং প্রয়োজনে গাছের কোণ ("মেদ") এর মতো সংখ্যাসূচক মানগুলি হয়।

এই অ্যালগরিদমের সবচেয়ে বড় হুমকি স্পষ্টতই (বৃহত সংখ্যায়) পাশের বা গাছের সামনের আলো (আরও অনুকূলিতকরণ পর্যন্ত বৃহত্তর সমস্যা) are সম্পাদনা (যুক্ত): এটি কী করতে পারে না: ক্রিসমাস ট্রি আছে কিনা তা সন্ধান করুন, এক চিত্রে একাধিক ক্রিসমাস গাছ সন্ধান করুন, লাস ভেগাসের মাঝখানে একটি ক্রিস্টমাস গাছটি সঠিকভাবে সনাক্ত করুন, ভারী বাঁকানো ক্রিসমাস গাছগুলি সনাক্ত করুন, উলটা বা ডাউন কাটা ...;)

বিভিন্ন পর্যায়ে হ'ল:

  • প্রতিটি পিক্সেলের জন্য যুক্ত করা উজ্জ্বলতা (আর + জি + বি) গণনা করুন
  • প্রতিটি পিক্সেলের উপরে 8 টি প্রতিবেশী পিক্সেলের এই মান যুক্ত করুন
  • সমস্ত পিক্সেলকে এই মান দিয়ে র্যাঙ্ক করুন (সবচেয়ে উজ্জ্বলতম) - আমি জানি, সত্যিই সূক্ষ্ম নয় ...
  • উপরে থেকে শুরু করে এগুলির মধ্যে N নির্বাচন করুন, খুব নিকটে থাকা এড়িয়ে যাওয়া
  • গণনা করুন এই শীর্ষ এন এর (আমাদের গাছের আনুমানিক কেন্দ্র দেয়)
  • নির্বাচিত উজ্জ্বলতমদের থেকে শীর্ষতম আলোয়ের জন্য মধ্যবর্তী অবস্থান থেকে উপরের দিকে প্রশস্ত অনুসন্ধান মরীচিটি থেকে শুরু করুন (লোকেরা একেবারে শীর্ষে কমপক্ষে একটি আলোক রাখার প্রবণতা রাখে)
  • সেখান থেকে, কল্পনা করুন যে লাইনগুলি 60 ডিগ্রি বাম এবং ডান নীচের দিকে যাচ্ছে (ক্রিসমাস গাছগুলি সেই চর্বিযুক্ত হওয়া উচিত নয়)
  • উজ্জ্বল আলোগুলির 20% এই ত্রিভুজের বাইরে না হওয়া পর্যন্ত এই 60 ডিগ্রি হ্রাস করুন
  • গাছের নীচের দিকের অনুভূমিক সীমানাটি দিয়ে ত্রিভুজটির একেবারে নীচে আলোটি সন্ধান করুন
  • সম্পন্ন

চিহ্নগুলির ব্যাখ্যা:

  • গাছের কেন্দ্রে বড় রেড ক্রস: শীর্ষ এন উজ্জ্বল আলোগুলির মিডিয়ান
  • সেখান থেকে উপরের দিকে বিন্দুযুক্ত লাইন: গাছের শীর্ষের জন্য "অনুসন্ধানের মরীচি"
  • ছোট লাল ক্রস: গাছের উপরে
  • সত্যই ছোট লাল ক্রস: শীর্ষে সমস্ত এন উজ্জ্বল আলো
  • লাল ত্রিভুজ: ডি'উহ!

সোর্স কোড:

<?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;
}


?>

ছবি: উপরের বাম নিম্ন কেন্দ্র নিচে বামদিকে উপরের ডান উচ্চ কেন্দ্র নীচে ডানদিকে

বোনাস: একটি জার্মান Weihnachtsbaum, উইকিপিডিয়া থেকে Römerberg http://commons.wikimedia.org/wiki/File:Weihnachtsbaum_R%C3%B6merberg.jpg


17

আমি ওপেনসিভি দিয়ে পাইথন ব্যবহার করেছি।

আমার অ্যালগরিদম এরকম হয়:

  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) তে পরিবর্তন করি তবে আমি সমস্ত গাছের নীচে বাম দিকের ভাল ফলাফল পেয়েছি, এখানে চিত্র বর্ণনা লিখুন

আমার অ্যালগোরিদম ধরে নিয়েছে যে গাছে গাছে আলোক আছে এবং নীচে বাম গাছের উপরের অংশে অন্যের চেয়ে কম আলো রয়েছে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.