NCrunch – Jakie to, kurwa, dobre

Cytatem Youtubowego kucharza (Food Emperor) polecam wszystkim narzędzie wniesione do zespołu, w którym pracuję, przez nowego kolegę Sebastiana.

Wiem że o NCrunchu pisało już wielu. Jednak nikomu nie udało się mnie do niego przekonać, więc jest pewnie wielu innych sceptyków tego narzędzia i do nich właśnie jest ten artykuł.
Jest to narzędzie pełniące rolę sumienia dla osób stosujących TDD. Każda linijka kodu C#-owego jest oznaczona kropą: białą gdy brakuje testów ją pokrywajacych, czerwoną gdy jakiś test nie przechodzi i zieloną gdy wszystko jest OK.
Działanie NCruncha

Czytaj dalej NCrunch – Jakie to, kurwa, dobre

4Developers 2015 okiem Szoguna

Od kilku lat fundacja Proidea organizuje konferencję 4Developers. Uczestniczyłem w niej już kilkukrotnie i postanowiłem wybrać się także w tym roku.

W poprzednich latach konferencja odbywała się w różnych miastach, jednak w tym roku organizatorzy postanowili nie zmieniać dobrej miejscówki i podobnie jak rok temu jako lokalizację wybrali Hotelu Gromada w Warszawie.

Czytaj dalej 4Developers 2015 okiem Szoguna

Aktualizacja ASP.Net Identity do wesji 2

Ostatnimi czasy w jednym z projektów pociągnąłem sobie aktualizacje bibliotek przez Nuget’a. A że byłem leniwy to pociągnąłem je jak leci, nie patrząc co aktualizuje o change logu poszczególnych bibliotek nie mówiąc.

Nie róbcie tego w domu!
Jedną z aktualizacji była nowa wersja mechanizmu ASP.Net Identity. Aktualizacja zmieniła encje wykorzystywane przez mechanizm, nie zmieniła jednak bazy danych (być może ze względu na fakt iż nie korzystam z mechanizmu migracji).
Fakt ten można dość łatwo przeoczyć, wystarczy, że podczas testowania ręcznego nie wykona się żadnej operacji związanej z autoryzacją. Ponieważ miałem aktywną sesję sprzed aktualizacji nie musiałem się ani logować, ani rejestrować, wyjątek został więc zgłoszony tuż po wylogowaniu.

Czytaj dalej Aktualizacja ASP.Net Identity do wesji 2

Gdy HtmlHelper to za mało

Czytając ostatnio o rozszerzeniach do klasy HtmlHelper, stwierdziłem że w wraz z rozwojem projektu ich różnorodność może stać się trudna do ogarnięcia i przydałoby się je pogrupować, zorganizować.
Zacząłem się więc zastanawiać jak takie rozwiązanie mogłoby wyglądać. Zaznaczam przy tym że sam tego jeszcze nie doświadczyłem, a koncepcje dalej przedstawione są raczej propozycjami niż wskazówkami.

Czytaj dalej Gdy HtmlHelper to za mało

Sqlite – agregacja bez group by

Krótki wpis o błędzie znalezionym przeze mnie w Sqlite.

Jeżeli w zapytaniu umieści się funkcję agregacyjną i zwykłą kolumnę, a w zapytaniu nie wstawi się group by to zapytanie wykona się bez zgłoszenia błędu. Wynikiem takiego zapytania będzie jeden wiersz w którym funkcja agregacyjna zwróci poprawną wartość natomiast w kolumnie wyciągniętej ze "zwykłej" kolumny znajdzie się jakaś  wartość (prawdopodobnie pierwsza z brzegu). W SQL Serverze po prostu się wywala w innych silnikach nie sprawdzałem.

Co tak cicho o Build Stuff 2014?

W dniach 19-23 Listopada w Hotelu Radisson Blue Hotel Lietuva w Wilnie odbędzie się trzecia już edycja konferencji Build Stuff. Jako prelegenci wystąpi wielu znanych guru naszego półświatka min: Greg Young, Eric Evans, Hadi Hariri czy Bob Ashton.

O samej konferencji można sobie doczytać pod linkiem sam nie będę powielał informacji tam zawartych. To co mnie jednak najbardziej zastanawia to fakt iż w tym roku jedyne co mnie uratowało przed przegapieniem tego Eventu to twitter Grega Younga. Nie widziałem żadnego wpisu na blogu, żadnej dyskusji na forum, żadnych powiadomień z grup mailowych. A czas ucieka: skończyła się już rejestracja po niższej cenie, znaczna część prelegentów już zebrana, sprzedano znaczną część biletów.

Ze swojej strony mogę tylko powiedzieć że ja będę nie tylko na konferencji ale także na warsztatach zorganizowanych przy okazji i namawiam wszystkich do dołączenia do mnie.

0xDBE Nie taki straszny jak go malują

0xDBE jest nowym narzędziem do zarządzania bazami danych stworzonym przez JetBrains. Znając R# oraz słysząc opinie o inych ich produktach można mieć nadzieję że będzie dobre. Jednak zamiast mieć nadzieję lepiej po prostu sprawdzić, a można to zrobić dzięki "Early Access Program". Skorzystał z niego Maciek Aniserowicz i wyraził się dość niepochlebnie. A więc postanowiłem sprawdzić to narzędzie sam. 

MySql pod Ubuntu

Pobrałem pakę, rozpakowałem uruchomiłem plik <folder>/bin/xbde.sh.
Na początek dostałem ekran z pytaniem o import ustawień – nic nie importowałem.
Import konfiguracji

Następnie zostało mi wyświetlone okno wyboru skórki. Okno które nie mieści się na ekranie (pomimo rozdzielczości 1600×900), przyciski kreatora widać dopiero po maksymalizacji okna, wybrałem motyw "Dracula". 
Wybór skórki
Na drugim ekranie wybór koloru liter i tła tutaj także "Dracula". Trzeci krok wybór preferowanego silnika DB i hasło do magazynu haseł. Przycisk "Start using 0xDBE" jest nie źle.
wybór Czcionek

Okienko DataSource and drivers, dodaję więc nowe źródło danych (MySql). W prawym panelu zostaje wyświetlony formularz do uzupełnienia danych serwera łudząco podobny do tego w Eclipse Database Development.
Źródła danych i sterowniki
Jeżeli na serwerze MySQL nie ma jeszcze żadnej bazy, a co za tym idzie użytkowników innych niż root to można nie podać nazwy bazy danych.

Na ekranie jest także troszeczkę mało widoczny komunikat o braku sterownika. Po kliknięciu w link download zostaje on pobrany.
Formularz zawiera też przycisk "Test connection".
Po kliknięciu OK przeniosło mnie do ekranu podzielonego na dwie części drzewko po lewej pozwalające przeglądać obiety we wszystkich bazach na serwerze do których użytkownik ma dostęp. Jest to okno które w każdym następnym uruchomieniu środowiska będzie uruchamiane domyślnie.

Na start założyłem tabelę z użytkownikami. Już tutaj działa podpowiadanie składni.
Podopowiadanie składni
Co ciekawe zaraz po uruchomieniu zapytania zakładającego tabelę Tabela ta znalazła się w mechanizmie podpowiadania, bez żadnego odświeżania.
Podpowiadanie składni 2

Koleną zaletą jest podpowiadanie nazw kolumn przed wpisaniem tabeli w bloku from jest feature którego bardzo mi brakuje w czystym Management Studio. Jestem ciekawy czy ta funkcjonalność nie spowoduje spadku wydajności przy większej bazie.
Podpowiadanie składni 3
Ostatnim testem MySql jest sprawdzenie czy z poziomu tego środowiska można uruchomić zapytanie zawierające średnik w literale – da się.

Sql Server Windows 

Pobrałem paczkę ze strony, zainstalowałem kreatorem.
Podobnie jak w przypadku Ubuntu zostałem poproszony o wybranie między importem a nową konfiguracją. Podobnie Jak poprzednio mam możliwość wyboru skórki tym razem okno się zmieściło na ekranie.

Przyszła więc pora aby podłączyć się do Sql Servera, do wyboru mam 2 sterowniki wybrałem ten od M$. Tak samo jak w przypadku MySQL dociągnąłem sterownik z poziomu komunikatu na dole okna. No i chciałem się podłączyć do jakiejś bazy i tu pierwsza ściana: sterownik jdbc od M$ nie wspiera połączeń do localDB. Dobra mogę bez tego żyć zainstalowałem lokalnie SQL Server Express i postanowiłem działać dalej.

Maciek Aniserowicz mówił o problemach z domenowym zalogowaniem się do serwera, potraktowałem to trochę jak wyzwanie i udało się, ale nie było łatwo.

Po pierwsze odpowiedni connectionstring/JDBC URL jak zwał tak zwał.

jdbc:sqlserver://localhost\sqlexpres:1433;databaseName=pusta;integratedSecurity=true

Należy dodać że w tym miejscu olewam wszystkie pola typu host/port/database, prawdopodobnie da się te pola fajnie skonfigurować ale nie dzisiaj.

Niestety to jeszcze za mało żeby móc pisać zapytania. Pierwszym problemem jest fakt iż javowy sterownik stworzony przez M$ nie wspiera Shared Memory, natomiast instancja SQL Servera domyślnie ma wyłączone inne protokoły. Postanowiłem uruchomić protokół TCP/IP aby to osiągnąć należy:

– Uruchomić SQL Server Configuration Manager 
– Wybrać z drzewa SQL Server Network configuration – TCP IP – klikąć prawym przyciskiem myszy i wybrać Enable
– Ponownie prawy przycisk – Properties – zakładka IPAddresses – znależć element z ip 127.0.0.1 i ustawić Enable na true
– Przewinąć na sam dół Wyczyścić TCP Dynamic ports ustawić TCP Port – ja ustawiłem na 1433 

Na koniec trzeba zrestartować usługę SQL Server. W tym momencie prawdopodobnie do serwera da się dostać przez autoryzację SQL-ową zmieniając tylko URL'a na coś takiego:

jdbc:sqlserver://localhost\sqlexpres:1433;user=MyUserName;password=*****;

Jednak dla autoryzacji domenowej należy się jeszcze trochę pomęczyć.

Po pierwsze należy pobrać sterownik ręcznie ze strony http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774 i rozpakować go gdzieś w rozsądnym miejscu.
W zarządzaniu sterownikami wyrzucić sterownik pobrany przez 0xDBE i wskazać ścieżkę do sterownika ze strony – znajduje on się w katalogu enu, istnieje szansa że ten krok można pominąć.
Zarządzanie sterownikami

Dodać do zmiennej środowiskowej "Path" ścieżkę do katalogu <Katalog sterowników>\enu\auth\x86. 
W ww katalogu znajduje się natywna dll'ka z którą sterownik javowy próbuje się komunikować. Co ciekawe istnieje też jego wersja 64bitowa jednak u mnie nie działa.

Po tej gimnastyce zrestartowałem 0xDBE i zaczęło działać. Cała grzebanina w googlu i zabawa zajęły mi jakieś 3,5 godziny (Maćku najwyrażniej za szybko się poddałeś), więc stwierdziłem że nie chce mi się już męczyć z alternatywnym sterownikiem jTds.

Coś prostrzego – Sqlite

Kliknąłem w zielony "Plus" w lewym górnym rogu "Data Source" – "Sqlite" – "Xerial", próbowałem wybrać ścieżkę do pliku ale kontrolka wyboru pliku nie pozwala wybrać pliku nieistniejącego więc trudno wpisałem ją z palca. Pobrałem brakujący sterownik, "Test connection" – działa. 

Podsumowanie

Wygląda na to że 0xDBE będzie bardzo fajnym uniwersalnym narzędziem do zarządzania bazami danych przez developerów. Może się okazać że jest najlepsze z pośród narzędzi jakich do tej pory używałem do zarządzania MySql-em i Sqlitem. Co do SqlServera chłopaki z JetBrains mają jeszcze sporo pracy przed sobą (przede wszystkim w kwestii ułatwienia konfiguracji).

Nieoczekiwane zachowanie przeglądarek – wszystkich

Dzisiaj krótki wpis o zachowaniu wszystkich przeglądarek, które niezwykle mnie zaskoczyło.

W formularzu znajduje się pole tekstowe, które może być albo uzupełnione z palca, albo jego wartość może być wyliczona metodą javascriptową. Po wyliczeniu wartości pole ma zostać zablokowane (przy czym wystarczy blokada interfejsu).
"Co to za sztuka taki kawałek kodu postawić Łup Łup Łup jquery jako tako na kupę i gotowe".

$('#my-btn').click(function(e) {
var value = computeValue();
$('#my-field-id').attr('disabled', 'disabled').val(value);
});

Wydawałoby się że nic tu się nie da spier…ć, a jednak się da. Przy ustawieniu atrybutu disabled na elemencie input jego wartość nie jest wysyłana na serwer. I jest to zachowanie udokumentowane w w3schools

Rozwiązaniem tego problemu jest użycie atrybutu readonly zamiast disabled. Wadą tego rozwiązania jest brak wyszarzenia kontrolki (trzeba porzeźbić w css), co niekoniecznie musi się podobać biznesowi. Innym rozwiazaniem może być użycie pola ukrytego synchronizowanego z polem tekstowym. Oba rozwiązania mają wg mnie podobną pracochłonność.
Atrybut disabled nie wpływa na działanie metody val z jquery. Nie powinno też być problemów z bindingiem knockoutowym.

Amatorskie spojrzenie na EmguCV – rysowanie

Zagadnieniem do którego dotarłem w ramach moich eksploracji EmguCV jest rysowanie. 
Podstawowym narzędziem rysującym jest metoda Draw. Metoda ta ma 11 przeciążeń, które osobiście podzieliłbym na 3 grupy: Rysowanie kształtów, Wyświetlanie tekstu, Rysowanie sekwencji.

Rysowanie kształtów:

Metoda Draw posiada 7 dedykowanych metod przeznaczonych do rysowania podstawowych kształtów geometrycznych. Metody te jako pierwszy parametr przyjmują strukturę opisującą kształt oraz 2 dodatkowe parametry: kolor lini, oraz jej grubość. Przy czym grubość 0 lub mniej oznacza wypełnienie kształtu kolorem.

var rect = new System.Drawing.Rectangle(x, y, width, height);
image.Draw(rect, color, thickness);

I w zasadzie jedyne co mogę stwierdzić to fakt, że działa.

Przypadkiem w którym nie jest już tak różowo jest koło/okrąg ponieważ to coś 
Okrąg generowany metodą Draw
co powstaje w wyniku wywołania tego kodu:

CircleF c = new CircleF(new PointF(centerX, centerY), radius);
image.Draw(c, color, thickness);

ciężko nazwać okręgiem. Przydałoby się tutaj trochę antialiasingu, niestety klasa Image nie posiada żadnej metody ani właściwości gwarantującej taką funkcjonalność, a więc trzeba kombinować.
Pierwszym obejściem jakie znalazłem było wykorzystanie właściwości Bitmap która jest typu System.Drawing.Bitmap. Właściwość ta dla kolorów Brg, Brga oraz Gray zwraca wewnętrzną Bitmapę synchronizowaną dwustronnie z zawartością obrazu, dla pozostałych sposobów opisu koloru zwracana jest nowa/niesynchronizowana Bitmapa. Podejrzewam że ta właśnie synchronizacja jest przyczyną przegranej Emgu z metodą BitmapData w teście wydajności.  

Mając obiekt typu Bitmap możemy wykorzystać całe bogactwo biblioteki GDI+ w tym klasę Graphics. Narysowanie okręgu z antialiasingiem można wykonać takim kodem:

using (var graphics = Graphics.FromImage(image.Bitmap))
{
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
using (var pen = new Pen(color, thickness))
{
graphics.DrawEllipse(pen, x, y, diameter, diameter);
}
}

a efekt jego działania prezentuje się następująco
Okrąg generowany z wykorzystaniem GDI+

Drugim rozwiązaniem jakie udało mi się znaleźć jest użycie klasy cvInvoke. O ile klasa Image jest dobrze przemyślanym proxy do wielu funkcjonalności z OpenCV tak klasa cvInvoke ma na celu zapewnić dostęp do jak największej grupy metod, niekoniecznie trzymając się najlepszych wzorców. Większość (jak nie wszystkie) z metod tej klasy korzysta bezpośrednio z PInvoke, jest pośród nich także metoda cvImage.

var center = new Point(centerX, centerY);
int radius = dlg.Radius;
var color = new Bgr(); // Jakiś kolor
var lineType = Emgu.CV.CvEnum.LINE_TYPE.CV_AA;
CvInvoke.cvCircle(image.Ptr, center, radius, color.MCvScalar, thickness, lineType, 0);

Metoda jako pierwszy parametr pobiera wskaźnik, można go znaleźć w klasie Image<,> pod właściwością Ptr, drugim parametrem są współrzędne środka okręgu, a kolejnym jego promień.
Czwartym parametrem jest kolor, nie jest to jednak ani liczba opisująca kolor, ani obiekt z grupy kolorów tylko coś nazwane MCvScalar, jak widać w powyższym listingu wartość tą można uzyskać z koloru przy pomocy właściwości MCvScalar. 
Piątym parametrem jest grubość linii, interpretowana tak samo jak w metodzie Draw, a szóstym jej typ. Są 3 typy linii 4-połączeniowa, 8-połączeniowa (używana przez metodę Draw) oraz AntiAliasing. Wyniki metody cvCircle z różną wartością tego parametru (w powiększeniu) przedstawia grafika poniżej.
Okręgi generowane metodą z klasy CvInvoke

Organoleptycznie stwierdzam, w tym miejscu błąd w OpenCV ponieważ przy wybraniu opcji 4-połączeniowej wyraźnie widać połączenia po ukosie, których nie powinno być. 
Ostatnim parametrem jest przesunięcie służące do skalowania wynikowego okręgu jeżeli 1 piksel ma być równy 1 jednostce we współrzędnych środka i długości promienia należy ustawić ten parametr na 0.

Rysowanie napisów

Jak każda biblioteka graficzna EmguCV umożliwia umieszczanie napisów na grafice. Sama klasa Image posiada przeciążenie metody Draw która jako parametry pobiera:

  • napis który ma zostać narysowany
  • opis czcionki – struktura zawierająca dane o czcionce
  • pozycję tekstu
  • kolor

Rysowanie sekwencji

Sekwencja (Typ Seq<T>) jest kolekcją pozwalającym przechowywać elementy w sposób uporządkowany. Elementy można dodawać na końcu (Metoda Push) albo na początku (PushFront) sekwencji. Sekwencja wymaga wskazania Magazynu (klasa MemStorage).

using (var memStor = new MemStorage())
{
var seq = new Seq(memStor);
var rand = new Random();
for (int i = 0; i &lt; 100; i++)
{
var point = new Point(rand.Next(100), rand.Next(100));
seq.Push(point);
}
}

Metoda Draw posiada 3 metody do rysowania sekwencji punktów. W najprostrzym przypadku przyjmuje ona 3 parametry: wspomnianą sekwencję oraz grubość i kolor linii. Dwa ostatnie parametry są interpretowane analogicznie jak we wczesniejszych przeciążeniach. Co ciekawe przekazanie obiektu Seq<Point> wypełnionymi punktami sprawi, że grafika nie ulegnie zmianie (WTF?).
Po wczytaniu się w dokumentację znalazłem przyczynę. Sekwencja sama w sobie stanowi tylko abstrakcję dla innych implementacji. Jednak jako taka nie jest abstrakcyjna można ją tworzyć, można do niej dodawać elementy, jednak nie da się jej narysować. Kolejną ciekawostką jest fakt iż jedyną klasą dziedziczącą po sekwencji jest Contour<T>, a próba dziedziczenia i nadpisania jedynej wirtualnej metody InContour nic nie zmienia (podczas rysowania ta metoda nie jest nawet wywoływana). Na szczęście  użycie klasy Contour<T> daje efekt w postaci narysowania wielokąta. Którego wierzchołkami są punkty sekwencji. 

DrawPolyline

Jak sama nazwa wskazuje służy do rysowania wielu linii, czyli łamanej. Posiada 2 przeciążenia. Pierwsze z nich pobiera jako parametr tablicę punktów, flagę określającą czy domknąć łamaną (czyli zrobić z niej wielokąt), oraz kolor i grubość linii interpretowane tak samo jak przez metodę Draw. Z punktu widzenia programisty można tą metodę traktować jak uproszczoną wersję rysowania konturu.
Drugie przeciążenie różni się pierwszym parametrem jako który przyjmuje tablicę tablic punktów  i służy do rysowania wielu łamanych.

FillConvexPolygon

Metoda pozwala wypełnić kolorem wielokąt wypukły nie należy jednak używać tej metody do rysowania wielokątów wklęsłych albo figur z przecinającymi się krawędziami, metoda ta prawdopodobnie wykorzystuje algorytm skanowania linii (o ile dobrze pamiętam nazwę) wydajny ale ograniczony funkcjonalnie. Moim zdaniem metoda ta ma wąskie zastosowanie.

Przykładowe listingi prezentujące wykorzystanie metod jak  zawsze znajdują się w projekcie EmguExperiments

Amatorskie spojrzenie na EmguCV – operacja na pikselach oraz wydajność

Pomimo bardzo bogatego zestawu metod klasy Image nie robi ona wszystkiego. Więc istnieje konieczność operacji bezpośrednio na punktach składających się na obraz. EmguCV oferuje dwie możliwości:

  • Czytelną w której zostaje użyty indekser zwracający obiekt TColor(w przykładzie poniżej Bgr)

    var pixel = image[row, column];
    pixel.B = value;
    image[row, column] = pixel;
    
  • Mniej czytelną ale wydajnieszą, odwołanie następuje do obiektu Data indekser na nim pozwala pominąć operacje na obiekcie i operować bezpośrednio na tablicy znajdującej się pod spodem.

    byte bluePartOfPixel = image.Data[row, column, 0];
    image.Data[row, column, 0] = (byte)78;
    
  • Najwyższa pora sprawdzić wydajność Emgu CV. Postanowiłem zaimplementować metodę generującą negatyw zdjęcia na kilka sposobów:

    • GetPixel/SetPixel – metoda z góry skazana na przegraną, jej jedynymi zaletami są: nie zła czytelność kodu, oraz fakt że dostępna w .Net z pudełka
    • BitmapData – wydajna metoda, główny konkurent Emgu CV pod tym względem, oprócz wydajności posiada same wady: wymaga używania sekcji unsafe, jest mało czytelna i błędogenna. Ze względu na ostatnią cechę zamiast się bawić w ręczną implementację wykorzystałem gotowca ze Stacka
    • ColorMatrix – stare dobre GDI+ umożliwia dokonywania przekształceń matrycowych zarówno na orientacji pikseli jak i ich kolorów (potrzebna jest do tego klasa Graphics) implementację znalazłem pod tym samym linkiem, co w przypadku BitmapData
    • Pixel Access pierwsza wspomniana w tym poście metoda dostępów do pikseli w EmguCV
    • Pixel Access Fast – druga metoda
    • Metoda Not – metoda w klasie Image dedykowana generowaniu negatywu obrazu

    Testy wykonywałem na swoim laptopie (8 GB RAM, i5, dwie karty grafiki NVidia NVS 5400M oraz jakiś Intel). Test podzieliłem na 2 kroki Otwarcie pliku – polegające na wywołaniu konstruktora klasy z odpowiednim parametrem, oraz Wygenerowanie negatywu – właściwa robota. Odrzucałem wynik pierwszego uruchomienia Testu (podczas niego mogą wykonywać się operacje które nie występują przy kolejnych uruchomieniach). Test wykonałem tylko na trzech plikach, oraz uruchamiając program na różnych kartach grafiki (mam możliwość wskazania która karta powinna być wykorzystana).
    Metoda badań nie jest może idealna jednak mimo to daje całkiem ciekawe wyniki:

    Obraz 3702×2304 jpeg 916KB

    NVidia NVS 5400M

    GDI+ Emgu CV
    Metoda GetPixel/SetPixel BitmapData ColorMatrix Pixel Access Pixel Access Fast Metoda Not
    Otwarcie pliku 00.037 00.038 00.039 00.107 00.110 00.112
    Wygenerowanie negatywu 10.404 00.054 00.157 02.892 02.088 00.114

    Intel(R) HD Graphics 4000

    GDI+ Emgu CV
    Metoda GetPixel/SetPixel BitmapData ColorMatrix Pixel Access Pixel Access Fast Metoda Not
    Otwarcie pliku 00.037 00.036 00.038 00.110 00.113 00.110
    Wygenerowanie negatywu 10.470 00.053 00.157 03.071 02.296 00.112

    Obraz 4320×3240 jpeg 3,21 MB

    NVidia NVS 5400M

    GDI+ Emgu CV
    Metoda GetPixel/SetPixel BitmapData ColorMatrix Pixel Access Pixel Access Fast Metoda Not
    Otwarcie pliku 00.037 00.038 00.039 00.107 00.110 00.112
    Wygenerowanie negatywu 20.764 00.118 00.340 06.079 04.302 00.252

    Intel(R) HD Graphics 4000

    GDI+ Emgu CV
    Metoda GetPixel/SetPixel BitmapData ColorMatrix Pixel Access Pixel Access Fast Metoda Not
    Otwarcie pliku 00.088 00.087 00.086 00.248 00.244 00.247
    Wygenerowanie negatywu 20.644 00.120 00.344 06.041 04.066 00.252

    Obraz 5184×3456 jpeg 4,87 MB

    NVidia NVS 5400M

    GDI+ Emgu CV
    Metoda GetPixel/SetPixel BitmapData ColorMatrix Pixel Access Pixel Access Fast Metoda Not
    Otwarcie pliku 00:00.112 00:00.117 00:00.115 00:00.345 00:00.353 00:00.355
    Wygenerowanie negatywu 00:00.112 00:00.117 00:00.115 00:00.345 00:00.353 00:00.355

    Intel(R) HD Graphics 4000

    GDI+ Emgu CV
    Metoda GetPixel/SetPixel BitmapData ColorMatrix Pixel Access Pixel Access Fast Metoda Not
    Otwarcie pliku 00:00.114 00:00.119 00:00.118 00:00.348 00:00.351 00:00.342
    Wygenerowanie negatywu 00:00.114 00:00.119 00:00.118 00:00.348 00:00.351 00:00.342

    Zgodnie z moimi oczekiwaniami BitmapData pokonał inne metody GDI+. Wywołanie dedykowanej metody Not detronizuje zabawę bezpośrednio na Pikselach, jak dla mnie jest to jednoznaczne ze stwierdzeniem "Jak coś jest już napisane nie pisz tego sam".
    Bardzo interesujący natomiast jest fakt iż metoda BitmapData jest dwukrotnie szybsza od dedykowanej metody z Emgu CV. Jestem jednak daleki od stwierdzenia iż mój test udowadnia słabość tej biblioteki. Wskazany wynik może mieć wiele przyczyn, metoda Not może być na tyle prosta że Emgu CV nie ma co optymalizować (na co wskazywałby brak istotnych różnic przy uruchamianiu programu na różnych kartach grafiki), jednak ambitniejszych przekształceń po prostu nie chce mi się implementować "z palca" więc nie dokonam takiego porównania.
    Warto też zwrócić uwagę na fakt iż przekształcenie matrycowe także nie wypadło źle w zestawieniu, a jego czytelność jest przyzwoita (co nie zmienia faktu iż kod pisany z jego wykorzystaniem zawsze powinien być komentowany).
    Ostatnim elementem układanki jest czas pobrania danych z dysku. W przypadku Emgu CV odczytanie danych stanowi dużą część pracochłonności całej operacji. W moim teście popełniłem jeden błąd i większe zdjęcia wczytywałem z dysku SSD, a małe z talerzowego więc nie może on być podstawą wnioskowania o skalowalności tego rozwiązania.
    Całość kodu testu znajduje się pod adresem: https://github.com/szogun1987/EmguExperriments/tree/master/EmguExperiments/Performance