Bir Noel Ağacı nasıl tespit edilir? [kapalı]


382

Aşağıdaki görüntülerde görüntülenen Noel ağaçlarını tespit eden bir uygulamayı uygulamak için hangi görüntü işleme teknikleri kullanılabilir?

Tüm bu görüntüler üzerinde çalışacak çözümler arıyorum. Bu nedenle, haar kaskad sınıflandırıcıları veya şablon eşleştirme eğitimi gerektiren yaklaşımlar çok ilginç değildir.

Yalnızca Açık Kaynak teknolojilerini kullandığı sürece herhangi bir programlama dilinde yazılabilecek bir şey arıyorum . Çözüm, bu soruda paylaşılan görüntülerle test edilmelidir. Orada 6 giriş görüntüleri cevap bunların her işlem sonuçlarını göstermesi gerekir. Son olarak, her biri için çıkış görüntüsü olmalıdır kırmızı çizgiler tespit ağaç surround çizin.

Bu görüntülerdeki ağaçları programlı olarak nasıl tespit edersiniz?


3
Resimlerin bazılarını eğitim için kullanmamıza izin veriliyor mu yoksa sağlanan tüm görüntüler doğrulama için kullanılmalı mı? Her iki durumda da, serin rekabet: D
Hannes Ovrén

7
@karlphillip, bu görüntüleri test için ve diğer resimleri eğitim için kullanmamızı ister misiniz? Sadece eğitim setinin ne olduğu belli değil.
GilLevi

16
@karlphillip: Tavsiyem: "açık kaynak" gereksinimini bırakın. Hangi dili / çerçeveyi kullandığınız gerçekten önemli değil. Görüntü işleme / bilgisayar görme algoritmaları dil agnostiktir, bu nedenle MATLAB'da yazabiliyorsanız, kesinlikle OpenCV veya tercih ettiğiniz başka bir çerçeve yapabilirsiniz ... !
Aralık'ta Amro

2
@karlphillip hepinizi bu 'arayışınıza' katkıda bulunmak için harekete geçirdiğimiz için teşekkürler! Tek bir soruna kaç farklı yaklaşımın bulunabileceğini görmek için üretken, ancak en önemlisi birkaç saat geçirmek için harika bir fırsat oldu ... Umarım 1 Ocak için tekrar yaparsınız (belki bir 'kızak Noel Baba 'meydan okuma? ;-))
sepdek

2
Tamam, soruyu rekabet unsurlarını kaldırmak için yeniden yazdım. Bunun kendi başına iyi durmasına izin vermesi gerektiğini düşünüyorum.
Brad Larson

Yanıtlar:


184

İlginç ve diğerlerinden biraz farklı olduğunu düşündüğüm bir yaklaşımım var. Yaklaşımımdaki temel fark, diğerlerinden bazılarıyla karşılaştırıldığında, görüntü segmentasyon adımının nasıl gerçekleştirildiğidir - Python'un scikit-learn'undan DBSCAN kümeleme algoritmasını kullandım ; mutlaka tek bir net sentroidi bulunmayabilecek şekilsiz şekiller bulmak için optimize edilmiştir.

En üst seviyede yaklaşımım oldukça basittir ve yaklaşık 3 adıma ayrılabilir. Önce bir eşik (ya da aslında iki ayrı ve farklı eşiğin mantıksal "veya") uyguluyorum. Diğer cevapların çoğunda olduğu gibi, Noel ağacının sahnedeki daha parlak nesnelerden biri olacağını varsaydım, bu yüzden ilk eşik sadece basit bir tek renkli parlaklık testi; 0-255 ölçeğinde (siyahın 0 ve beyazın 255 olduğu) 220'nin üzerinde değerleri olan tüm pikseller, ikili siyah beyaz bir görüntüye kaydedilir. İkinci eşik, altı görüntünün sol üst ve sağ alt tarafındaki ağaçlarda özellikle belirgin olan kırmızı ve sarı ışıkları aramaya çalışır ve fotoğrafların çoğunda yaygın olan mavi-yeşil arka plana karşı iyi bir şekilde öne çıkar. RGB görüntüsünü hsv alanına çeviriyorum, ve renk tonunun 0.0-1.0 ölçeğinde (kabaca sarı ve yeşil arasındaki sınıra karşılık gelen) 0.2'den az veya 0.95'den (mor ve kırmızı arasındaki sınıra karşılık gelen) olmasını ve ayrıca parlak, doygun renklere gereksinim duymasını gerektirir: doygunluk ve değerin her ikisi de 0.7'nin üzerinde olmalıdır. İki eşik prosedürünün sonuçları mantıksal olarak "veya" - bir araya getirilir ve elde edilen siyah beyaz ikili görüntülerin matrisi aşağıda gösterilmiştir:

Noel ağaçları, HSV'de eşiklendikten sonra tek renkli parlaklık

Her görüntünün, kabaca her ağacın konumuna karşılık gelen büyük bir piksel kümesine sahip olduğunu açıkça görebilirsiniz, ayrıca bazı görüntülerin bazılarında, bazı binaların pencerelerindeki ışıklara veya bir arka plan sahne ufukta. Bir sonraki adım, bilgisayarın bunların ayrı kümeler olduğunu tanımasını sağlamak ve her pikseli bir küme üyelik kimlik numarasıyla doğru bir şekilde etiketlemektir.

Bu görev için DBSCAN'ı seçtim . Burada bulunan diğer kümeleme algoritmalarına göre DBSCAN'ın tipik olarak nasıl davrandığına dair oldukça iyi bir görsel karşılaştırma var . Daha önce söylediğim gibi, amorf şekillerle iyi sonuç verir. DBSCAN çıktısı, her kümenin farklı bir renkte çizilmesiyle burada gösterilir:

DBSCAN kümeleme çıktısı

Bu sonuca bakarken dikkat etmeniz gereken birkaç nokta var. Birincisi, DBSCAN'ın, davranışın düzenlenmesi için algoritmanın bir test noktasını toplamak yerine yeni bir ayrı küme bildirmesi için bir çift noktanın ne kadar ayrılması gerektiğini etkili bir şekilde kontrol eden bir "yakınlık" parametresi ayarlamasını gerektirmesidir. önceden var olan bir küme. Bu değeri, her görüntünün diyagonalinde 0,04 kat olarak ayarladım. Görüntülerin boyutu kabaca VGA'dan yaklaşık HD 1080'e kadar değiştiğinden, bu tür ölçeğe bağlı tanım kritiktir.

Dikkat çeken bir başka nokta, scikit-learn uygulamasında uygulandığı gibi DBSCAN algoritmasının, bu örnekteki daha büyük görüntülerin bazıları için oldukça zor olan bellek sınırlarına sahip olmasıdır. Bu nedenle, daha büyük görüntülerin bir kısmı için, bu sınırın içinde kalmak için aslında her kümeyi "yok etmek" (yani, yalnızca her 3. veya 4. pikselde tutmak ve diğerlerini bırakmak) gerekiyordu. Bu ayıklama işleminin bir sonucu olarak, kalan tek tek seyrek piksellerin bazı büyük görüntülerde görülmesi zordur. Bu nedenle, yalnızca görüntüleme amacıyla, yukarıdaki görüntülerdeki renk kodlu pikseller etkili bir şekilde "genişletilmiştir", böylece daha iyi görünürler. Bu sadece anlatı uğruna kozmetik bir operasyon; kodumda bu genişlemeden bahseden yorumlar olsa da,

Kümeler tanımlandıktan ve etiketlendikten sonra, üçüncü ve son adım kolaydır: Her görüntüdeki en büyük kümeyi basitçe alıyorum (bu durumda, toplam üye pikseli sayısı açısından "boyutu" ölçmeyi seçtim, ancak biri bunun yerine, fiziksel kapsamı ölçen bir tür metriği kolayca kullanabilir) ve bu küme için dışbükey gövdeyi hesaplar. Dışbükey gövde daha sonra ağaç sınırı haline gelir. Bu yöntemle hesaplanan altı dışbükey gövde aşağıda kırmızı renkle gösterilmiştir:

Hesaplanan sınırları ile Noel ağaçları

Kaynak kodu Python 2.7.6 için yazılmıştır ve bağlı numpy , SciPy , matplotlib ve scikit-öğrenirler . İki parçaya ayırdım. İlk bölüm gerçek görüntü işlemeden sorumludur:

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

ve ikinci bölüm, ilk dosyayı çağıran ve yukarıdaki tüm grafikleri oluşturan kullanıcı düzeyinde bir betiktir:

#!/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'un çözümü kümelenme. (k-means)
user3054997

1
@stachyra Ayrıca daha basit olanlarımı teklif etmeden önce bu yaklaşımı düşündüm. Bunun, diğer durumlarda da iyi sonuçlar üretmek için genişletilmesi ve yaygınlaştırılması için büyük bir potansiyeli olduğunu düşünüyorum. Kümeleme için sinir ağlarını deneyebilirsiniz. Bir SOM veya nöral gaz gibi bir şey mükemmel bir iş çıkarır. Yine de, büyük teklif ve benden Yaşasın!
Eylül 13:47

4
@Faust ve Ryan Carlson: teşekkürler çocuklar! Evet, yukarı oy sisteminin, birbirinden birkaç saat içinde gönderilen 2 veya 3 kısa cevap arasında karar vermek için iyi çalışmasına rağmen, uzun süreler boyunca oynayan uzun cevaplarla yarışmalar söz konusu olduğunda ciddi önyargılara sahip olduğunu kabul ediyorum. . Birincisi, erken gönderimler daha sonra kamuoyunun incelemesine bile sunulmadan önce oylar biriktirmeye başlar. Ve cevapların hepsi uzunsa, o zaman mütevazı bir ipucu oluşturduğu anda, insanlar geri kalanını okumak için uğraşmadan sadece ilkini yükseltirken genellikle bir "bandwagon etkisi" vardır.
stachyra

2
@stachyra harika bir haber arkadaşı! En sıcak tebrikler ve bu yeni yılın başlangıcı olsun!
Ocak14

1
@ lennon310: Bu problemin üstünde henüz yerel maksimum algılama filtresi denemedim ama bunu kendiniz keşfetmek istiyorsanız, scipy içeren bu bir . Bu proje için Python kaynak kodum o kadar kısaydı ki aslında% 100'ünü yayınlayabiliyordum; kelimenin tam anlamıyla yapmanız gereken tek şey, iki kod parçacığımı kopyalayıp yapıştırmak için ayrı .py dosyalarına yapıştırmak ve daha sonra scipy.ndimage.filters.maximum_filter()bir eşiği kullandığım yerde bir çağrının yerine koymaktır .
stachyra

145

DÜZENLEME NOT: Bu yayını, (i) her ağaç görüntüsünü gereksinimlerden istendiği gibi ayrı ayrı işlemek için düzenledim, (ii) sonucun kalitesini artırmak için hem nesne parlaklığını hem de şeklini göz önünde bulundurun.


Aşağıda nesnenin parlaklığını ve şeklini dikkate alan bir yaklaşım sunulmaktadır. Başka bir deyişle, üçgen benzeri bir şekle ve önemli bir parlaklığa sahip nesneleri arar. Java'da Marvin görüntü işleme çerçevesi kullanılarak uygulandı .

İlk adım renk eşiğidir. Buradaki amaç, analizi önemli ölçüde parlaklığa sahip nesnelere odaklamaktır.

çıktı görüntüler:

kaynak kodu:

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

İkinci adımda, şekiller oluşturmak için görüntüdeki en parlak noktalar genişletilir. Bu işlemin sonucu, önemli parlaklığa sahip nesnelerin olası şeklidir. Taşkın dolgu segmentasyonu uygulandığında, bağlantısı kesilen şekiller algılanır.

çıktı görüntüler:

kaynak kodu:

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

Çıktı görüntüsünde gösterildiği gibi, birden çok şekil tespit edildi. Bu problemde, görüntülerde sadece birkaç parlak nokta var. Ancak, bu yaklaşım daha karmaşık senaryolarla başa çıkmak için uygulanmıştır.

Bir sonraki adımda her şekil analiz edilir. Basit bir algoritma, üçgene benzer bir desene sahip şekilleri algılar. Algoritma, nesne şeklini satır satır analiz eder. Her şekil çizgisinin kütlesinin merkezi hemen hemen aynı ise (bir eşik verilir) ve y arttıkça kütle artarsa, nesnenin üçgen benzeri bir şekli vardır. Şekil çizgisinin kütlesi, o çizgideki şekle ait piksel sayısıdır. Nesneyi yatay olarak dilimlediğinizi ve her bir yatay segmenti analiz ettiğinizi düşünün. Birbirlerine merkezileşirlerse ve uzunluk ilk segmentten lineer bir desende sonuncuya yükselirse, muhtemelen bir üçgene benzeyen bir nesneniz vardır.

kaynak kodu:

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

Son olarak, her şeklin bir üçgene benzer ve önemli bir parlaklığa sahip konumu, bu durumda bir Noel ağacı, aşağıda gösterildiği gibi orijinal görüntüde vurgulanır.

son çıkış görüntüleri:

nihai kaynak kodu:

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

Bu yaklaşımın avantajı, nesne şeklini analiz ettiği için muhtemelen diğer aydınlık nesneleri içeren görüntülerle çalışmasıdır.

Mutlu Noeller!


NOT 2'yi DÜZENLE

Bu çözümün ve diğer bazılarının çıktı görüntülerinin benzerliği hakkında bir tartışma var. Aslında çok benzerler. Ancak bu yaklaşım sadece nesneleri parçalamakla kalmaz. Nesne şekillerini bir anlamda analiz eder. Aynı sahnede birden fazla parlak nesneyi işleyebilir. Aslında, Noel ağacının en parlak olması gerekmez. Sadece tartışmayı zenginleştirmek için onu terk ediyorum. Örneklerde sadece en parlak nesneyi arayan bir önyargı var, ağaçları bulacaksınız. Ancak, bu noktada tartışmayı gerçekten durdurmak istiyor muyuz? Bu noktada, bilgisayar bir Noel ağacına benzeyen bir nesneyi gerçekten ne kadar tanıyor? Bu boşluğu kapatmaya çalışalım.

Aşağıda bu noktayı açıklamak için bir sonuç sunulmuştur:

girdi resmi

resim açıklamasını buraya girin

çıktı

resim açıklamasını buraya girin


2
İlginç. Umarım her görüntü ayrı ayrı işlendiğinde aynı sonuçları elde edebilirsiniz. Soruyu daha önce belirtmek için 4 saat önce düzenledim. Cevabınızı bu sonuçlarla güncelleyebilmeniz harika olurdu.
karlphillip

@Marvin üçgen tespitinizde, kütle dalgalanmalarını nasıl ele aldınız? Sıkı bir üçgen değil, y değiştikçe kütle mono değil
user3054997

2
@ user3054997: Bu başka bir nokta. Gönderdiğim gibi, algoritma katı üçgen şekilleri aramıyor. Her nesneyi analiz eder ve basit bir ölçütle bir üçgene "benzeyen" bir ağacı düşünür: nesnenin kütlesi, y arttıkça artmak için kullanılır ve her yatay nesne segmentinin kütlesi merkezi birbirine neredeyse merkezileştirilir. .
Gabriel Ambrósio Archanjo

@Marvin Çözümüm gerçekten basit, cevabımda da söyledim. Bu arada ilk çözümünüzden daha iyi çalıştı. Doğru hatırlıyorsam, ilk cevabınızda, burada yaptığınız şey değil, küçük ışık dokusunu tespit etmek için özellik tanımlayıcılarından bahsettiniz. Basitçe, şimdiki yaklaşımınızın ve sonuçlarınızın benim için ilk çözümünüzden çok daha benzer olduğunu söyledim. Tabii ki kabul etmenizi beklemiyorum, sadece kayıt için söyledim.
smeso

1
@sepdek Burada benimkinden çok daha iyi olan birkaç çözüm var ve hala benim oylarımın yarısını alıyorlar. Diğer çözümlerden "ilham alma" konusunda yanlış bir şey yoktur. Ben de senin çözümlerini gördüm, sana karşı söyleyecek bir şeyim yok, onları benden sonra gönderdin ve benim "fikrim" sadece beni kopyaladığını söyleyecek kadar orijinal değildi. Ama Marvin, benden önce yayınlanan ve aynı algoritmayı kullanarak benimkini gördükten sonra çözüm olan tek kişiydi ... en azından "Evet, çözümün hoşuma gitti ve tekrar kullandım" demişti, yanlış bir şey yok, sadece bir oyun.
smeso

75

İşte benim basit ve aptal çözümüm. Ağacın resimdeki en parlak ve büyük şey olacağı varsayımına dayanmaktadır.

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

İlk adım resimdeki en parlak pikselleri tespit etmektir, ancak ağacın kendisi ile ışığını yansıtan kar arasında bir ayrım yapmak zorundayız. Burada renk kodları üzerinde gerçekten basit bir filtre uygulayan karı hariç tutmaya çalışıyoruz:

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

Sonra her "parlak" pikseli buluruz:

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

Sonunda iki sonuca katılıyoruz:

bitwise_and(tmp, tmp1, tmp1);

Şimdi en büyük parlak nesneyi arıyoruz:

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

Şimdi neredeyse yaptık, ama kar nedeniyle hala bazı kusurlar var. Onları kesmek için, istenmeyen parçaları silmek için bir ağacın şekline yaklaşık olarak yaklaşmak üzere bir daire ve bir dikdörtgen kullanarak bir maske oluşturacağız:

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

Son adım, ağacımızın konturunu bulmak ve orijinal resme çizmektir.

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

Üzgünüm ama şu anda kötü bir bağlantım var, bu yüzden resim yüklemem mümkün değil. Daha sonra yapmaya çalışacağım.

Mutlu Noeller.

DÜZENLE:

İşte son çıktının bazı resimleri:


1
Merhaba! Cevabınızın tüm gereksinimleri karşıladığından emin olun: 6 giriş resmi vardır ve cevap her birinin işlenmesinin sonuçlarını göstermelidir; .
karlphillip

Selam! Sen benim programa CLI argümanlar olarak dosya adlarını geçirebilirsiniz: ./christmas_tree ./*.png. İstediğiniz kadar olabilirler, sonuçlar herhangi bir tuşa basıldıktan sonra art arda gösterilecektir. Bu yanlış mı?
smeso

Tamam, ancak yine de görüntüleri yüklemeniz ve bunları sorunuzda paylaşmanız gerekiyor, böylece ileti dizisinin izleyicileri gerçekten sonucunuzu görebiliyor . İnsanların ne yaptığınızı görmelerine izin vermek, oy alma şansınızı artıracaktır;)
karlphillip

Bunun için bir çözüm bulmaya çalışıyorum, bazı bağlantı sorunlarım var.
smeso

2
Harika! Şimdi bunları aşağıdaki kodla cevap içinde yeniden ölçeklendirebilirsiniz: <img src="http://i.stack.imgur.com/nmzwj.png" width="210" height="150">Sadece resme bağlantıyı değiştirin;)
karlphillip

60

Kodu Matlab R2007a'da yazdım. Noel ağacını kabaca çıkarmak için k-araçları kullandım. Ara sonucumu yalnızca bir resim ve altı sonucun tümü ile göstereceğim.

İlk olarak, RGB alanını Lab kanalına eşledim, b kanalındaki kırmızı kontrastını artırabilir:

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

resim açıklamasını buraya girin

Renk uzayındaki özelliğin yanı sıra, her pikselden ziyade mahalle ile ilgili doku özelliğini de kullandım. Burada 3 orijinal kanaldan (R, G, B) yoğunluğu doğrusal olarak birleştirdim. Bu şekilde biçimlendirmemizin nedeni, resimdeki yılbaşı ağaçlarının üzerinde kırmızı ışıklar ve bazen de yeşil / bazen mavi aydınlatma olması.

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

resim açıklamasını buraya girin

Üzerine 3X3 yerel ikili desen uyguladım I0, eşik olarak orta pikseli kullandım ve eşiğin üzerindeki ortalama piksel yoğunluğu değeri ile altındaki ortalama değer arasındaki farkı hesaplayarak kontrastı elde ettim.

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

resim açıklamasını buraya girin

Toplamda 4 özelliğim olduğundan, kümeleme yöntemimde K = 5'i seçerdim. K-araçlarının kodu aşağıda gösterilmiştir (Dr. Andrew Ng'in makine öğrenimi kursundan alınmıştır. Daha önce dersi aldım ve kodu programlama görevine kendim yazdım).

[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

Program bilgisayarımda çok yavaş çalıştığı için 3 yineleme çalıştırdım. Normalde durdurma kriterleri (i) en az 10 tekrarlama süresi veya (ii) artık sentroidlerde değişiklik olmamasıdır. Testime göre, yinelemenin artırılması arka planı (gökyüzü ve ağaç, gökyüzü ve bina, ...) daha doğru bir şekilde farklılaştırabilir, ancak Noel ağacının çıkarılmasında önemli bir değişiklik göstermedi. Ayrıca, k-araçlarının rastgele centroid başlatmaya bağışık olmadığını unutmayın, bu nedenle bir karşılaştırma yapmak için programı birkaç kez çalıştırmak önerilir.

K-vasıtasından sonra, maksimum yoğunluğu olan etiketli bölge I0seçildi. Sınırları çıkarmak için sınır izleme kullanılmıştır. Bana göre, son Noel ağacı çıkarılması en zor olanı çünkü bu resimdeki kontrast ilk beşte olduğu gibi yeterince yüksek değil. Metodumdaki bir başka konu da bwboundaries, sınırı izlemek için Matlab'da işlevi kullandığım , ancak bazen iç sınırların da 3., 5., 6. sonuçlarda gözlemleyebileceğiniz gibi dahil edilmesidir. Noel ağaçlarındaki karanlık taraf sadece aydınlatılmış tarafla kümelenmekle kalmadı, aynı zamanda çok sayıda küçük iç sınırın izlenmesine de yol açıyor ( imfillçok fazla iyileşmiyor). Tüm algoritma hala çok geliştirme alanı var.

Bazı yayınlar ortalama kaydırmanın k-araçlarından daha sağlam olabileceğini ve birçok grafik kesimi tabanlı algoritmanın karmaşık sınır segmentasyonunda çok rekabetçi olduğunu göstermektedir. Ortalama bir algoritma yazdım, bölgelerin yeterince ışık olmadan daha iyi çıkarıldığı görülüyor. Ancak ortalama değişim biraz fazla bölümlere ayrılmıştır ve birleştirme stratejisine ihtiyaç vardır. Bilgisayarımda k-araçlarından bile daha yavaş koştu, korkarım ki vazgeçmeliyim. Yukarıda bahsedilen modern algoritmalarla diğerlerinin burada mükemmel sonuçlar vereceğini merakla bekliyorum.

Yine de her zaman özellik seçiminin görüntü segmentasyonunda anahtar bileşen olduğuna inanıyorum. Nesne ve arka plan arasındaki kenar boşluğunu en üst düzeye çıkarabilen uygun bir özellik seçimi ile, birçok segmentasyon algoritması kesinlikle işe yarayacaktır. Farklı algoritmalar sonucu 1'den 10'a yükseltebilir, ancak özellik seçimi 0'dan 1'e yükseltebilir.

Mutlu Noeller !


2
Cevap için teşekkürler! Sadece Matlab'ın açık kaynak olmadığını , ancak Scilab'ın olduğunu belirtmek istedim . Ben de bu cevabın diğerleriyle yarıştığını görmek isterdim. ;)
karlphillip

6
Teşekkürler Karl. Octave, Matlab ile neredeyse aynı kodlama gramerini paylaşan başka bir açık kaynaklı yazılımdır: mathworks.fr/matlabcentral/answers/14399-gnu-octave-vs-matlab .
lennon310

İlginç, bunu bilmiyordum, teşekkürler! Kodunuz Octave üzerinde çalışıyor mu?
karlphillip

Henüz test etmedim, ama bence sorun yok :)
lennon310 27:13

@ lennon310 Sanırım sınırları düşürüp dışbükey gövdeyi alırsanız, delik probleminden kurtulacaksınız. Dışbükey gövdenin bir kümedeki tüm noktaları içeren en küçük alan olduğunu unutmayın.
Sepdek

57

Bu, geleneksel görüntü işleme yaklaşımlarını kullanan son görevim ...

Burada bir şekilde diğer iki teklifimi birleştirerek daha da iyi sonuçlar elde ediyorum . Aslında bu sonuçların nasıl daha iyi olabileceğini göremiyorum (özellikle yöntemin ürettiği maskeli görüntülere baktığınızda).

Yaklaşımın merkezinde üç temel varsayımın kombinasyonu vardır :

  1. Görüntüler ağaç bölgelerinde yüksek dalgalanmalara sahip olmalıdır
  2. Görüntüler ağaç bölgelerinde daha yüksek yoğunluğa sahip olmalıdır
  3. Arka plan bölgelerinin yoğunluğu düşük olmalı ve çoğunlukla mavi renkte olmalıdır

Bu varsayımlar göz önünde bulundurulduğunda, yöntem aşağıdaki gibi çalışır:

  1. Görüntüleri HSV'ye dönüştürme
  2. V kanalını bir LoG filtresiyle filtreleme
  3. 'Etkinlik' maskesi A'yı elde etmek için LoG filtreli görüntüye sabit eşikleme uygulayın
  4. Yoğunluk maskesi B'yi elde etmek için V kanalına sert eşik uygulayın
  5. Arka plan maskesine C düşük yoğunluklu blue-ish bölgelerini yakalamak için H kanal eşiği uygulayın
  6. Son maskeyi almak için AND ile maskeleri birleştirin
  7. Bölgeleri büyütmek ve dağınık pikselleri bağlamak için maskeyi genişletin
  8. Küçük bölgeleri ortadan kaldırın ve sonunda sadece ağacı temsil edecek son maskeyi alın

İşte MATLAB'daki kod (yine, komut dosyası geçerli klasördeki tüm jpg görüntülerini yükler ve yine bu optimize edilmiş bir kod parçası olmaktan çok uzaktır):

% 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

Sonuçlar

Sonuçlar

Yüksek çözünürlüklü sonuçlar hala burada!
Ek görüntülerle daha fazla deney burada bulunabilir.


1
Harika şeyler! Lütfen diğer yanıtlarınızın da bu biçimi izlediğinden emin olun. Ödül için yarışmak için açık kaynaklı bir teknoloji kullanmalısınız ve ne yazık ki Matlab bunlardan biri değil. Ancak, SciLab ve Octave benzer sözdizimi ve işlevleri sağlarlar. ;)
karlphillip

Oktav kodu aynı ...
sepdek

@karlphillip Bir şekilde bu soru bir Matlab etiketine sahip oldu. Açık kaynak gerçekten bir zorunluluksa onu kaldırmanızı tavsiye ederim.
Dennis Jaheruddin

@sepdek Çok güzel, belki de yine son resimde 'delikler' dahil etmek için bir şeyler yapılabilir. (Tamamen onaylanmış piksellerle çevrili tüm pikselleri
eklensin

1
@karlphillip teşekkürler adam! Yaklaşımımı ilginç bulduğunuz için mutluyum. Buna ek olarak, en zarif çözümü seçtiğin için en çok oyu verdiğiniz için sizi tebrik ediyorum !!!
Ocak14

36

Çözüm adımım:

  1. R kanalını al (RGB'den) - bu kanalda yaptığımız tüm işlemler:

  2. İlgi Çekici Bölge Oluştur (YG)

    • Minimum 149 değeri olan eşik R kanalı (sağ üst görüntü)

    • Sonuç bölgesini genişlet (sol ortadaki resim)

  3. Hesaplanan yatırım getirisindeki yaşları tespit edin. Ağacın birçok kenarı var (sağ ortadaki resim)

    • Dilate sonucu

    • Daha büyük yarıçaplı erozyon (sol alt resim)

  4. En büyük (alana göre) nesneyi seçin - sonuç bölgesi

  5. ConvexHull (ağaç dışbükey çokgendir) (sağ alt resim)

  6. Sınırlayıcı kutu (sağ alt resim - grren kutusu)

Adım adım: resim açıklamasını buraya girin

İlk sonuç - en basit ama açık kaynaklı yazılım değil - "Adaptive Vision Studio + Adaptive Vision Library": Bu açık kaynak kodlu değil, prototip oluşturmak için çok hızlı:

Noel ağacını tespit etmek için tüm algoritma (11 blok): AVL çözümü

Sonraki adım. Açık kaynaklı bir çözüm istiyoruz. AVL filtrelerini OpenCV filtrelerine değiştir: Burada küçük değişiklikler yaptım, örneğin Edge Detection, cvCanny filtresini kullanıyordu, roi'ye göre, bölge görüntüsünü kenar görüntüsüyle çarptığımda, en büyük öğeyi bulduğum findContours + contourArea öğesini seçti, ancak fikir aynı.

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

OpenCV çözümü

Şimdi ara adımları olan görüntüleri gösteremiyorum çünkü sadece 2 bağlantı koyabiliyorum.

Tamam şimdi openSource filtrelerini kullanıyoruz ama hala açık kaynak değil. Son adım - c ++ koduna bağlantı noktası. 2.4.4 sürümünde OpenCV kullandım

Son c ++ kodunun sonucu: resim açıklamasını buraya girin

c ++ kodu da oldukça kısadır:

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

Hangi derleyici bu programı hatasız olarak oluşturabilir?
karlphillip

Oluşturmak için Visual Studio 2012 kullandım. C ++ 11 desteği ile c ++ derleyicisini kullanmalısınız.
AdamF

Bununla ilgili elimde bir sistem yok. std::max_element()Aramayı yeniden yazabilir misiniz ? Cevabınızı da ödüllendirmek istiyorum. Bence gcc 4.2 var.
karlphillip

Tamam bu c ++ 11 özelliğidir;) Yukarıdaki kaynak kodunu değiştirdim. Lütfen şimdi dene.
AdamF

Pekala, teşekkürler. Test ettim ve çok güzel. Bu soru tekrar açılır açılmaz (diğer kullanıcılar bana bu konuda yardımcı olmalı) Seni ödüllendirmek için başka bir ödül ayarlayabilirim. Tebrikler!
karlphillip

31

... başka bir eski moda çözüm - tamamen HSV işlemeye dayalı :

  1. Görüntüleri HSV renk aralığına dönüştürme
  2. HSV'deki buluşsal yöntemlere göre maskeler oluşturun (aşağıya bakın)
  3. Bağlantısı kesilen alanları bağlamak için maskeye morfolojik dilatasyon uygulayın
  4. Küçük alanları ve yatay blokları atın (ağaçların dikey bloklar olduğunu unutmayın)
  5. Sınırlayıcı kutuyu hesapla

HSV işleminde buluşsal yöntemler hakkında bir kelime :

  1. Tonlar (H) 210 - 320 derece arasında olan her şey , arka planda veya ilgili olmayan alanlarda olması gereken mavi macenta olarak atılır
  2. Değerler (V)% 40'tan düşük olan her şey de alakalı olamayacak kadar karanlık olarak atılır

Elbette bu yaklaşıma ince ayar yapmak için çok sayıda başka olasılık denenebilir ...

İşte hile yapmak için MATLAB kodu (uyarı: kod optimize edilmekten çok uzak! Sadece MATLAB programlama için sadece süreçteki herhangi bir şeyi takip edebilmek için tavsiye edilmeyen teknikler kullandım - bu büyük ölçüde optimize edilebilir):

% 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

Sonuçlar:

Sonuçlarda maskeli görüntüyü ve sınırlayıcı kutuyu gösteririm. resim açıklamasını buraya girin


Merhaba, cevap için teşekkürler. Lütfen yanıtınızın tüm talimatları izlediğinden emin olmak için bir dakikanızı ayırarak Gereksinimler bölümünü okuyun . Ortaya çıkan resimleri paylaşmayı unuttunuz. ;)
karlphillip

2
@karlphillip sepdek, görüntüleri paylaşmak için yeterli üne sahip değil, resimleri bağlantısına ve talimatlarına göre cevap gövdesine taşıdım. Ancak bunların doğru olanlar olduğundan emin değilim, bu kısmı yorumlamaktan çekinmeyin.
alko

@alko biliyorum, teşekkürler. Ancak paylaştığınız bazı resimler giriş kümesinde değildi . Cevap, soru üzerinde paylaşılan 6 resmin tümünü işlemenin sonucunu göstermelidir.
karlphillip

@karlphillip bu onun görüntüleri, benim değil. "Bu bölümü yorum yap" derken kastettiğim şey bu;)
alko

2
Sorunlara neden olduğum için üzgünüm ... niyetim değil. İlk veri kümesindeki tüm görüntüleri dahil ettim ve konseptimin sağlam olduğunu kanıtlamak için daha da geliştirdim ...
sepdek

23

Bazı eski moda görüntü işleme yaklaşımı ...
Fikir, görüntülerin yanan ağaçları genellikle daha koyu ve daha yumuşak arka planlarda (veya bazı durumlarda ön planlarda) tasvir ettiği varsayımına dayanmaktadır . Işıklı ağaç alanı daha "enerjik" dir ve daha yüksek yoğunluğa sahiptir .
İşlem aşağıdaki gibidir:

  1. Graylevel'e dönüştür
  2. En "etkin" alanları elde etmek için LoG filtrelemesi uygulayın
  3. En aydınlık alanları elde etmek için yoğun bir eşik uygulayın
  4. Ön maskeyi almak için önceki 2'yi birleştirin
  5. Alanları büyütmek ve komşu bileşenleri bağlamak için morfolojik bir dilatasyon uygulayın
  6. Küçük aday alanları alan boyutlarına göre ortadan kaldırın

Elde ettiğiniz her resim için bir ikili maske ve sınırlayıcı kutu.

İşte bu naif tekniği kullanan sonuçlar: resim açıklamasını buraya girin

MATLAB üzerindeki kod şu şekildedir: Kod, JPG görüntüleri olan bir klasörde çalışır. Tüm görüntüleri yükler ve algılanan sonuçları döndürür.

% 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'un yaptığı gibi ortaya çıkan görüntüleri yüklemeyi unutmayın.
karlphillip

Burada bir çaylağım, bu yüzden resim yükleyemiyorum. Lütfen açıklamamda sağlanan bağlantılardaki sonuçları görün.
sepdek

Tamam, ama hala herkesin yaptığı gibi soru üzerinde paylaşılan görüntüleri kullanmanız gerekiyor. Onları işledikten sonra, bir yere yükleyin ve bağlantıları eklemek için cevabınızı düzenleyin. Daha sonra cevabınızı düzenleyeceğim ve görüntüleri sizin için yerleştireceğim.
karlphillip

Bağlantı şimdi doğru resimleri içeriyor gibi görünüyor.
Dennis Jaheruddin

22

Gördüğümden oldukça farklı bir yaklaşım kullanarak, Noel ağaçlarını ışıklarıyla algılayan bir senaryo. Sonuç her zaman simetrik bir üçgen ve gerekirse ağacın açısı ("şişmanlık") gibi sayısal değerler olur.

Bu algoritmaya yönelik en büyük tehdit, açıkça (çok sayıda) veya ağacın önündeki ışıklardır (daha fazla optimizasyona kadar daha büyük sorun). Düzenleme (eklendi): Yapamayacakları: Bir Noel ağacı olup olmadığını öğrenin, bir görüntüde birden fazla Noel ağacı bulun, Las Vegas'ın ortasında bir Noel ağacını doğru bir şekilde tespit edin, çok bükülmüş Noel ağaçlarını tespit edin, baş aşağı veya doğranmış ...;)

Farklı aşamalar:

  • Her piksel için eklenen parlaklığı (R + G + B) hesaplayın
  • Her bir pikselin üzerine 8 komşu pikseli de ekleyin
  • Tüm pikselleri bu değere göre sırala (önce en parlak) - Biliyorum, gerçekten ince değil ...
  • Yukarıdan başlayarak, çok yakın olanları atlayarak bunlardan N'yi seçin
  • Hesapla Bu üst N (bize ağacın yaklaşık merkezini verir)
  • Seçilen en parlak ışıklardan en üstteki ışık için medyan konumundan yukarı doğru genişleyen bir arama ışığında başlayın (insanlar en üste en az bir ışık koyma eğilimindedir)
  • Oradan, 60 derece sola ve sağa doğru giden çizgiler hayal edin (yılbaşı ağaçları o kadar şişman olmamalı)
  • En parlak ışıkların% 20'si bu üçgenin dışına çıkana kadar 60 dereceyi azaltın
  • Üçgenin en altındaki ışığı bulun ve size ağacın alt yatay kenarlığını verin
  • Bitti

İşaretlerin açıklaması:

  • Ağacın ortasındaki büyük kızıl haç: En iyi N parlak ışığın medyanı
  • Oradan yukarı doğru noktalı çizgi: ağacın üstü için "arama huzmesi"
  • Daha küçük kızıl haç: ağacın üstü
  • Gerçekten küçük kırmızı haçlar: En iyi N parlak ışığın tümü
  • Kırmızı üçgen: D'uh!

Kaynak kodu:

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


?>

Görüntüler: Sol üst Alt merkez Sol alt Sağ üst Üst merkez Sağ alt

Bonus: Wikipedia'dan bir Alman Weihnachtsbaum, Römerberg http://commons.wikimedia.org/wiki/File:Weihnachtsbaum_R%C3%B6merberg.jpg


17

Opencv ile python kullandım.

Algoritmam şöyle gider:

  1. İlk olarak kırmızı kanalı görüntüden alır
  2. Kırmızı kanala bir eşik (min. Değer 200) uygulayın
  3. Sonra Morfolojik Gradyan uygulayın ve sonra bir 'Kapanış' yapın (dilatasyon ve ardından Erozyon)
  4. Sonra düzlemdeki konturları bulur ve en uzun konturu alır.

Sonuç:

Kod:

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

Çekirdeği (25,5) 'den (10,5)' e değiştirirsem, sol alt hariç tüm ağaçlarda daha iyi sonuçlar alırım, resim açıklamasını buraya girin

algoritmam ağacın üzerinde ışık olduğunu ve sol alt ağaçta üstte diğerlerinden daha az ışık olduğunu varsayar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.