Amatorskie spojrzenie na EmguCV

EmguCV jest .Netowym wrapperem do OpenCV – potężnej biblioteki do przetwarzania i analizy obrazów (zarówno statycznych jak i ruchomych) oraz innych sygnałów cyfrowych.

Ważną cechą biblioteki jest fakt iż stara się ona wykorzystać jak najlepiej zasoby dostępne na maszynie na której jest ona uruchomiona, ze szczególnym uwzględnieniem karty grafiki.

Oprócz tego OpenCV wspiera machine learning, jednak tej części biblioteki jeszcze nie dotykałem.

Co ma wspólnego programista .Net zajmujący się nudnymi dokumentami korporacyjnymi z przetwarzaniem obrazów? Niewiele. Po prostu od czasu do czasu trzeba spróbować czegoś nowego for-fun więc znalazłem pierwszy lepszy pretekst żeby pobawić się się EmguCV.

Od razu zaznaczę, że nie czytałem o żadnych dobrych praktykach, ani nawet nie zagłębiałem się w tutoriale tylko postanowiłem rozpocząć naukę przez eksplorację wspartą przez google i stackoverflow. W dodatku skoncentrowałem się tylko na podstawowych przekształceniach obrazów statycznych.

1. Instalacja

Instalację należy zacząć od wejścia na stronę http://sourceforge.net/projects/emgucv/ i pobrania instalatora i klikania next, next, next. Już sam rozmiar paczki instalacyjnej robi wrażenie (200+MB), Natomiast po jej zainstalowaniu stracimy prawie 2GB dysku. Liczbę tą można zmniejszyć rezygnując z instalacji przykładowych solucji.

2. Pierwsze podpięcie

Podłączenie Emgu do solucji w celu przekształcania obrazów statycznych jest opisane na stronie http://www.emgu.com/wiki/index.php/Setting_up_EMGU_C_Sharp zgodnie z tym tutorialem należy do swojego projektu dodać referencje do dll’ek EmguCV.dll, EmguCV.UI.dll oraz EmguCV.Util.dll biblioteki te znajdują się w katalogu “<ścieżka_do_emgu>\bin”.

Biblioteki te nie są jednak samodzielnymi bibliotekami tylko wrapperami do bibliotek napisanych w c++ które posiadają wersje 32 i 64 bitową co za tym idzie nasza aplikacja albo będzie miała 2 wersje albo nie będzie działać na niektórych stacjach roboczych.

Powoduje to że powinniśmy ustawić właściwość “Target platform” na “x64” (Properties w naszym csproj, zakładka Build).

Drugim problemem o którym wspomniany wcześniej tutorial nie mówi to fakt iż zależności między bibliotekami się trochę pozmieniały i dodanie do projektu tylko i wyłącznie opencv_core<ver>.dll i opencv_imgproc<ver>.dll (znajdujacych się w katalogu  “<ścieżka_do_emgu>\bin\x64”) sprawi że po wykorzystaniu jakiejkolwiek metody z biblioteki EmguCV dostaniemy w twarz błędem o mało mówiącym komunikacie “'System.TypeInitializationException' occurred in Emgu.CV.dll”. Grzebiąc po internecie znalazłem poradę aby po prostu przekopiować cały katalog “<ścieżka_do_emgu>\bin\x64” do swojej solucji tak aby lądował w katalogu bin\Debug wadą tego rozwiązania jest jednak fakt iż cały katalog waży 566MB. Jeżeli ktoś zna minimalny podzbiór dll’ek potrzebny do otworzenia obrazu z pliku i wyświetlenia go w Picturebox będę wdzięczny.

3. Pokaż cy…kod

Podstawową klasą z której korzystałem podczas moich zabaw jest Image<TColor, TDepth> jak widać jest to klasa generyczna pierwszy parametr oznacza w jaki sposób można operować na kolorach obrazu. Przykładami typów które można tam postawić są Bgr, Gray, Bgr556, Hsv i kilka innych. Parametr TDepth oznacza typ używany do określenia głębi kolorów (osobiście zawsze tam wrzucam byte).

Klasa ta posiada kilka kontruktorów z których według mnie najważniejsze to:

public Image(Bitmap bmp);

Służy do konwesji bitmapy z przestrzeni nazw System.Drawing na obraz który nadaje się do przetworzenia przez Emgu.

public Image(string fileName)

Pozwala otworzyć obraz z pliku, metoda nie wspiera formatów Uri, jednak wnioskując po treści zgłaszanego wyjątku obsługa taka zostanie dodana

public Image(int width, int height, TColor value);

Tworzy obraz o zadanych wymiarach i kolorze

Niestety pośród konstruktorów brakuje otwarcia obrazu z dowolnego strumienia, jednak można się posiłkować następującym obejściem:

using (Stream stream = new SomeStream())
            {
                using (var memoryStream = new MemoryStream())
                {
                    stream.CopyTo(memoryStream);
                    memoryStream.Position = 0;

                    Image image = Image.FromRawImageData(memoryStream.ToArray());
                }
            }

Klasa Image zawiera szereg metod które można podzielić na dwie grupy:

  • zaczynające się od podkreślenia – zmieniają obiekt obrazu na którym są wywoływane

  • nazywające się jak pan Bóg przykazał – generują kopię obrazu z naniesionymi zmianami

Część metod posiada wersję z podkreśleniem i bez. Intuicja mi podpowiada że metody “podkreślnikowe” są wydajniejsze ale nie są nie można ich używać bezpiecznie w środowisku wielowątkowym (nie szukałem jednak potwierdzenia tej tezy w dokumentacji).

Pierwszą metodą w klasie Image, którą postanowiłem przybliżyć jest Convert<TColor, TDepth>  metoda da pozwala na stworzenie obrazu opisanego w inny sposób lub mającego inną głębię.

// Dzięki temu możemy operować na głębi i nasyceniu kolorów
var image2 = image.Convert<Hsv, byte>();
// Konwersja na skalę szarości - nieodwracalna
var image3 = image.Convert<Gray, byte>();

Drugą ważną dla mnie metodą jest .ToBitmap pozwalająca konwertować obraz do klasy System.Drawing.Bitmap co pozwala wyświetlić wynik przekształceń w kontrolkach Windows Forms.