394 Stimmen

Wie erkennt man einen Weihnachtsbaum?

Welche Bildverarbeitungstechniken könnten verwendet werden, um eine Anwendung zu implementieren, die die Weihnachtsbäume erkennt, die in den folgenden Bildern angezeigt werden?

Ich suche nach Lösungen, die mit allen diesen Bildern funktionieren. Daher sind Ansätze, die das Training von haar cascade classifiers oder template matching erfordern, nicht sehr interessant.

Ich suche nach etwas, das in beliebiger Programmiersprache geschrieben werden kann, solange sie nur _Open Source_Technologien verwendet. Die Lösung muss mit den Bildern getestet werden, die in dieser Frage geteilt sind. Es gibt 6 Eingabebilder und die Antwort soll die Ergebnisse der Verarbeitung jedes einzelnen anzeigen. Schließlich müssen für jedes Ausgabebild rote Linien gezeichnet werden, um den erkannten Baum zu umgeben.

Wie würden Sie programmgerecht die Bäume in diesen Bildern erkennen?

75voto

smeso Punkte 4185

Hier ist meine einfache und dumme Lösung. Sie basiert auf der Annahme, dass der Baum das hellste und größte Ding auf dem Bild sein wird.

//g++ -Wall -pedantic -ansi -O2 -pipe -s -o weihnachtsbaum weihnachtsbaum.cpp `pkg-config --cflags --libs opencv`
#include 
#include 
#include 

using namespace cv;
using namespace std;

int main(int argc,char *argv[])
{
    Mat original,tmp,tmp1;
    vector  > 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"< 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;
}

Der erste Schritt besteht darin, die hellsten Pixel im Bild zu erkennen, aber wir müssen eine Unterscheidung zwischen dem Baum selbst und dem Schnee treffen, der sein Licht reflektiert. Hier versuchen wir, den Schnee auszuschließen, indem wir einen wirklich einfachen Filter auf die Farbcodes anwenden:

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

Dann finden wir jeden "hell" Pixel:

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

Zuletzt verbinden wir die beiden Ergebnisse:

bitwise_and(tmp, tmp1, tmp1);

Jetzt suchen wir nach dem größten hellen Objekt:

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

Jetzt sind wir fast fertig, aber es gibt immer noch einige Unvollkommenheiten aufgrund des Schnees. Um sie abzuschneiden, erstellen wir eine Maske, die einen Kreis und ein Rechteck verwendet, um die Form eines Baumes näherungsweise darzustellen, um unerwünschte Teile zu löschen:

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

Der letzte Schritt besteht darin, den Umriss unseres Baumes zu finden und ihn auf das Originalbild zu zeichnen.

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

Entschuldigung, aber im Moment habe ich eine schlechte Verbindung, daher ist es mir nicht möglich, Bilder hochzuladen. Ich werde versuchen, es später zu tun.

Frohe Weihnachten.

EDIT:

Hier sind einige Bilder des Endergebnisses:

62voto

lennon310 Punkte 12523

Ich habe den Code in Matlab R2007a geschrieben. Ich habe k-Means verwendet, um den Weihnachtsbaum grob zu extrahieren. Ich zeige mein Zwischenergebnis nur mit einem Bild und das Endergebnis mit allen sechs.

Zuerst habe ich den RGB-Raum auf den Lab-Raum abgebildet, was den Kontrast von Rot in seinem b-Kanal verbessern konnte:

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

Bildbeschreibung hier eingeben

Abgesehen vom Merkmal im Farbraum habe ich auch texturbezogene Eigenschaften verwendet, die mit der Nachbarschaft zusammenhängen, anstatt mit jedem Pixel selbst. Hier habe ich die Intensität aus den 3 Originalkanälen (R, G, B) linear kombiniert. Der Grund, warum ich das auf diese Weise formatiert habe, ist, dass die Weihnachtsbäume auf dem Bild alle rote Lichter haben und manchmal grüne/manchmal blaue Beleuchtung.

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

Bildbeschreibung hier eingeben

Ich habe ein 3x3 lokales Binärmuster auf I0 angewendet, den Zentralpixel als Schwellenwert verwendet und den Kontrast erhalten, indem ich den Unterschied zwischen dem mittleren Pixelintensitätswert über dem Schwellenwert und dem Mittelwert unterhalb berechnet habe.

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))); % Kontrast
    end
end

Bildbeschreibung hier eingeben

Da ich insgesamt 4 Merkmale habe, würde ich K=5 in meine Clustering-Methode wählen. Der Code für k-Means wird unten gezeigt (er stammt aus dem Maschinenlernkurs von Dr. Andrew Ng. Ich habe den Kurs zuvor absolviert und den Code in seiner Programmieraufgabe selbst geschrieben).

[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    
      % Für jedes Beispiel in X, weise es dem nächstgelegenen Zentroiden zu
      idx = findClosestCentroids(X, centroids);

      % Gegeben die Zugehörigkeiten, berechne neue Zentroide
      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, :);
      % Finde den nächstgelegenen Zentroiden für 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

Da das Programm auf meinem Computer sehr langsam läuft, habe ich nur 3 Iterationen durchgeführt. Normalerweise ist das Abbruchkriterium (i) mindestens 10 Iterationen oder (ii) keine weiteren Änderungen an den Zentroiden mehr. Meiner Meinung nach kann das Erhöhen der Iterationen das Hintergrund (Himmel und Baum, Himmel und Gebäude usw.) genauer differenzieren, zeigte jedoch keine drastischen Änderungen bei der Extraktion des Weihnachtsbaums. Beachten Sie auch, dass k-Means nicht immun gegen die zufällige Zentroideninitialisierung ist. Es wird empfohlen, das Programm mehrmals auszuführen, um einen Vergleich anzustellen.

Nach dem k-Means wurde die markierte Region mit der maximalen Intensität von I0 ausgewählt. Die Konturverfolgung wurde verwendet, um die Grenzen zu extrahieren. Der letzte Weihnachtsbaum ist für mich der schwierigste zu extrahieren, da der Kontrast in diesem Bild nicht so hoch ist wie in den ersten fünf. Ein weiteres Problem bei meiner Methode ist, dass ich die Funktion bwboundaries in Matlab verwendet habe, um die Grenze zu verfolgen, aber manchmal werden auch innere Grenzen mit einbezogen, wie Sie in den 3., 5. und 6. Ergebnissen sehen können. Die dunkle Seite innerhalb der Weihnachtsbäume wurde nicht nur versäumt, mit der beleuchteten Seite gruppiert zu werden, sondern führte auch zu vielen winzigen inneren Grenzverfolgungen (imfill verbessert nicht sehr viel). Insgesamt hat mein Algorithmus immer noch viel Verbesserungspotenzial.

Einige Veröffentlichungen deuten darauf hin, dass Mean-Shift möglicherweise robuster ist als k-Means, und viele graphenbasierte Algorithmen sind auch sehr Wettbewerbsfähig bei komplizierten Grenzsegmentierungen. Ich habe einen Mean-Shift-Algorithmus selbst geschrieben. Er scheint die Regionen besser zu extrahieren, die nicht genug Licht haben. Aber Mean-Shift ist etwas übersegmentiert, und einige Strategien zur Zusammenführung sind erforderlich. Auf meinem Computer lief er sogar noch langsamer als k-Means, ich fürchte, ich muss ihn aufgeben. Ich freue mich darauf zu sehen, ob andere hier ausgezeichnete Ergebnisse mit den oben genannten modernen Algorithmen einreichen werden.

Doch ich glaube immer noch, dass die Merkmalsauswahl der Schlüsselbestandteil bei der Bildsegmentierung ist. Mit einer geeigneten Merkmalsauswahl, die den Abstand zwischen Objekt und Hintergrund maximieren kann, werden viele Segmentierungsalgorithmen definitiv funktionieren. Unterschiedliche Algorithmen können das Ergebnis von 1 auf 10 verbessern, aber die Merkmalsauswahl kann es von 0 auf 1 verbessern.

Frohe Weihnachten!

58voto

sepdek Punkte 1402

Dies ist mein letzter Beitrag, in dem die herkömmlichen Bildverarbeitungsansätze verwendet werden...

Hier kombiniere ich irgendwie meine beiden anderen Vorschläge, um noch bessere Ergebnisse zu erzielen. Tatsächlich sehe ich nicht, wie diese Ergebnisse besser sein könnten (besonders wenn man sich die maskierten Bilder ansieht, die die Methode produziert).

Im Herzen des Ansatzes steht die Kombination von drei Schlüsselannahmen:

  1. Bilder sollten hohe Fluktuationen in den Baumregionen haben
  2. Bilder sollten eine höhere Intensität in den Baumregionen haben
  3. Hintergrundregionen sollten eine niedrige Intensität haben und größtenteils bläulich sein

Mit diesen Annahmen funktioniert die Methode wie folgt:

  1. Die Bilder in HSV umwandeln
  2. Den V-Kanal mit einem LoG-Filter filtern
  3. Auf den mit LoG-gefilterten Bildern eine harte Schwellenwertbildung anwenden, um eine 'Aktivitäts'-Maske A zu erhalten
  4. Eine harte Schwellenwertbildung auf dem V-Kanal anwenden, um eine Intensitätsmaske B zu erhalten
  5. Den H-Kanal schwellenwertbasiert anwenden, um niedrigintensive bläuliche Regionen in die Hintergrundmaske C aufzunehmen
  6. Masken mit UND verknüpfen, um die endgültige Maske zu erhalten
  7. Die Maske erweitern, um Regionen zu vergrößern und verstreute Pixel zu verbinden
  8. Kleine Regionen eliminieren und die endgültige Maske erhalten, die schließlich nur den Baum darstellen wird

Hier ist der Code in MATLAB (nochmals, das Skript lädt alle jpg-Bilder im aktuellen Ordner und ist weit entfernt von einem optimierten Code):

% alles löschen
clear;
pack;
close all;
close all hidden;
drawnow;
clc;

% Initialisierung
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,
    % Originalbild laden
    imgs{end+1}=imread(ims(i).name);

    % In HSV-Farbraum umwandeln
    images{end+1}=rgb2hsv(imgs{i});

    % Laplacian-Filterung anwenden und eine heuristische harte Schwellenwertbildung durchführen
    val_thres = (max(max(images{i}(:,:,3)))/thres_div);
    log_image{end+1} = imfilter( images{i}(:,:,3),fspecial('log')) > val_thres;

    % Die hellsten Regionen des Bildes erhalten
    int_thres = 0.26*max(max( images{i}(:,:,3)));
    int_image{end+1} = images{i}(:,:,3) > int_thres;

    % Die wahrscheinlichsten Hintergrundregionen des Bildes erhalten
    back_image{end+1} = images{i}(:,:,1)>(150/360) & images{i}(:,:,1)<(320/360) & images{i}(:,:,3)<0.5;

    % Die endgültige binäre Abbildung durch Kombination von hoher 'Aktivität' mit hoher Intensität berechnen
    bin_image{end+1} = logical( log_image{i}) & logical( int_image{i}) & ~logical( back_image{i});

    % Morphologische Dilatation anwenden, um getrennte Komponenten zu verbinden
    strel_size = round(0.01*max(size(imgs{i})));        % Strukturelement für die morphologische Dilatation
    dilated_image{end+1} = imdilate( bin_image{i}, strel('disk',strel_size));

    % Messungen durchführen, um kleine Objekte zu eliminieren
    measurements{i} = regionprops( logical( dilated_image{i}),'Area','BoundingBox');

    % Iterative Vergrößerung des Strukturelements für eine bessere Konnektivität
    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
    % Sicherstellen, dass das dilatierte Bild dieselbe Größe wie das Originalbild hat
    dilated_image{i} = dilated_image{i}(1:size(imgs{i},1),1:size(imgs{i},2));
    % Begrenzungsrahmen berechnen
    [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

%%% zusätzlicher Code zur Anzeige von Dingen
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

Ergebnisse

Ergebnisse

Ergebnisse mit hoher Auflösung sind immer noch hier verfügbar!
Noch mehr Experimente mit zusätzlichen Bildern finden Sie hier.

37voto

AdamF Punkte 2479

Meine Lösungsschritte:

  1. Holen Sie sich den R-Kanal (aus RGB) - alle Operationen führen wir auf diesem Kanal aus:

  2. Region von Interesse (ROI) erstellen

    • Den R-Kanal mit dem Mindestantwert 149 thresholden (oben rechtes Bild)

    • Das Ergebnisgebiet erweitern (Mitte links Bild)

  3. Kanten im berechneten ROI erkennen. Der Baum hat viele Kanten (Mitte rechtes Bild)

    • Ergebnis erweitern

    • Mit größerem Radius erodieren (unteres linkes Bild)

  4. Das größte Objekt (nach Fläche) auswählen - dies ist das Ergebnisgebiet

  5. ConvexHull (Baum ist ein konvexes Polygon) (unteres rechtes Bild)

  6. Begrenzungsrahmen (unteres rechtes Bild - grüner Rahmen)

Schritt für Schritt: Bildbeschreibung hier eingeben

Das erste Ergebnis - das einfachste, aber nicht in Open-Source-Software - "Adaptive Vision Studio + Adaptive Vision Library": Dies ist zwar nicht Open-Source, aber sehr schnell bei der Prototypenerstellung:

Gesamter Algorithmus zur Erkennung von Weihnachtsbäumen (11 Blöcke): AVL-Lösung

Nächster Schritt. Wir wollen eine Open-Source-Lösung. Ändern von AVL-Filtern zu OpenCV-Filtern: Hier habe ich kleine Änderungen vorgenommen, z. B. die Kantenerkennung verwendet den cvCanny-Filter, um das ROI zu respektieren, habe ich das Regionsbild mit dem Kantenbild multipliziert, um das größte Element auszuwählen, habe ich findContours + contourArea verwendet, aber die Idee ist die gleiche.

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

OpenCV-Lösung

Ich kann jetzt keine Bilder mit Zwischenschritten zeigen, da ich nur 2 Links einfügen kann.

Jetzt verwenden wir Open-Source-Filter, aber es ist immer noch nicht ganz offen. Letzter Schritt - Umsetzung des Codes in C++. Ich habe OpenCV in der Version 2.4.4 verwendet

Das Ergebnis des endgültigen C++-Codes ist: Bildbeschreibung hier eingeben

Der C++-Code ist auch recht kurz:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include 
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 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 > contours, contours1(1);
        vector convex;
        vector 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& A, const vector& 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;
}

31voto

sepdek Punkte 1402

...eine weitere altmodische Lösung - rein basierend auf der HSV-Verarbeitung:

  1. Bilder in den HSV-Farbraum umwandeln
  2. Masks gemäß der Heuristik im HSV erstellen (siehe unten)
  3. Morphologische Dilatation auf die Maske anwenden, um getrennte Bereiche zu verbinden
  4. Kleine Bereiche und horizontale Blöcke verwerfen (denken Sie daran, dass Bäume vertikale Blöcke sind)
  5. Das Bounding-Box berechnen

Ein Wort zu den Heuristiken bei der HSV-Verarbeitung:

  1. Alles mit Farben (H) zwischen 210 - 320 Grad wird als blau-magenta verworfen, was sich im Hintergrund oder in nicht relevanten Bereichen befindet
  2. Alles mit Werten (V) unter 40% wird ebenfalls als zu dunkel verworfen, um relevant zu sein

Natürlich kann man mit zahlreichen anderen Möglichkeiten experimentieren, um diesen Ansatz zu verfeinern...

Hier ist der MATLAB-Code, um den Trick zu vollführen (Warnung: Der Code ist weit davon entfernt, optimiert zu sein!!! Ich habe Techniken verwendet, die nicht für die MATLAB-Programmierung empfohlen werden, nur um im Prozess irgendetwas verfolgen zu können - dies kann stark optimiert werden):

% alles löschen
clear;
pack;
close all;
close all hidden;
drawnow;
clc;

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

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

for i=1:num, 
    % ursprüngliches Bild laden
    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');
    % in den HSV-Farbraum umwandeln
    hsvs{end+1} = rgb2hsv(imgs{i});
    % harte Schwelle anwenden und binäre Operation zur Erstellung der Maske durchführen
    masks{end+1} = medfilt2( ~(hsvs{i}(:,:,1)>(210/360) & hsvs{i}(:,:,1)<(320/360))&hsvs{i}(:,:,3)>0.4);
    % morphologische Dilatation anwenden, um getrennte Komponenten zu verbinden
    strel_size = round(0.03*max(size(imgs{i})));        % Strukturelement für morphologische Dilatation
    dilated_images{end+1} = imdilate( masks{i}, strel('disk',strel_size));
    % Messungen durchführen, um kleine Objekte zu eliminieren
    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));
    % Bounding-Box berechnen
    [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 

%%% zusätzlicher Code zur Anzeige von Dingen
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])));

Ergebnisse:

In den Ergebnissen zeige ich das maskierte Bild und die Bounding Box. Bildbeschreibung hier eingeben

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X