Amatorskie spojrzenie na EmguCV – podstawowe przekształcenia obrazów

W trzeciej części serii chciałbym ukazać łatwość z jaką przy pomocy EmguCV można dokonywać przekształceń obrazu. W tym poście skoncentruję się na 3 przekształceniach: Odbicie lustrzane (Flip), Obrocie, Skalowaniu.
Jednak zanim przejdę do właściwego tematu chciałbym wspomnieć o ręcznym zarządzaniu zasobami. Klasa Image<TColor, TDepth> implementuje interfejs IDisposable co wskazuje na konieczność zwalniania zasobów gdy tylko obiekt przestaje nam być potrzebny. Jednak dokumentacja Emgu mówi o braku takiej konieczności (prawdopodobnie jest to związane z faktem iż Emgu poprawnie implementuje ten interfejs a sama klasa posiada finalizer zwalniający zasoby gdy przyjdzie odpowiednia pora. Ja osobiście domyślnie wywołuję Dispose (czy to jawnie czy to przez konstrukcję using), chyba że mam wątpliwości czy zwalniany obiekt nie jest gdzieś jeszcze używany, dlatego w swoim projekciku raczej nie będę się stosował do zaleceń dostawcy.
 
Aby zrobić odbicie lustrzane obrazu należy na nim wywołać metodę Flip (albo _Flip), metoda ta jako parametr pobiera wartość FLIP.HORIZONTAL albo FLIP.VERTICAL gdzie FLIP jest Enum'em.
Przy okazji wrócę do tematu metod "podreślnikowych". Kilka moich eksperymentów potwierdziło że ich używanie zużywa mniej pamięci operacyjnej niż używanie ich odpowiedników bez podkreślnika na początku. Drugim spostrzeżeniem jakiego przy ich okazji dokonałem to fakt iż kontrolka ImageBox nie obserwuje zmian w obrazie i po wywołaniu metody z podkreślnikiem należy albo podstawić właściwość Image kontrolki ponownie albo wywołać na niej Invalidate. 
Wadą API Emgu CV jaką widać na przykładzie metod podkreślnikowych i ww. enuma jest nie trzymanie się konwencji nazewniczych przyjętych w C#.
Jednak odbicie lustrzane nie jest niczym nadzwyczajnym (nawet klasa System.Drawing.Bitmap posiada metodę RotateFlip), dlatego przechodzę do czegoś co w Windows Forms jest gorzej wspierane, a więc obrót. W Emgu CV dokonuje się go metodą Rotate (jakie zaskakujące), metoda ta nie posiada swojego "podkreślnikowego" odpowiednika prawdopodobnie ze względu na fakt iż zmienia ona rozmiar obrazu. Metoda ta posiada 3 przeciążenia w najprostrzym przypadku parametrami tej metody jest kąt obrotu w stopniach oraz kolor jakim zostaną wypełnione "dziury" w obrazie powstałe w wyniku niedopasowania, dodatkowo można zdefiniować czy wynikowy obraz ma być obcięty do oryginalnych rozmiarów (domyślnie obraz jest obcięty), środek obrotu – punkt niestety nie udało mi się ustalić sposobu jego interpretacji, a w dokumentacji którą znalazłem brakowało opisu tego przeciążenia metody Rotate. Ostatnim parametrem jest sposób interpolacji – pomijając obroty o wielokrotności 90 stopni jasnym jest że po obrocie piksele oryginalnego obrazu nie trafią idealnie w piksele obrazu wynikowego, więc kolory pikseli wynikowych należy obliczyć na podstawie kolorów pikseli które wylądowały w pobliżu. W zastosowaniach profesjonalnych może parametr ten może mieć znaczenie, jednak organoleptycznie nie udało mi się dostrzec różnic pomiędzy wynikami zastosowanymi z różnymi wynikami tego parametru.
Skalowanie obrazu wykonuje się przy pomocy metody Resize. Posiada ona 3 przeciążenia, wszystkie 3 mają parametr interpolationType interpretowany analogicznie jak taki sam parametr w obrocie. Najprostrze przeciążenie przyjmuje oprócz tego parametru także skalę. Drugie przeciążenie zamiast skali przyjmuje docelową szerokość i wysokość obrazu – przy czym obraz może się skalować inaczej wzdłóż osi X, a inaczej wzdłóż osi Y. Ostatnie przeciążenie do drugiego przeciążenia dodaje parametr preserveScale ustawienie go na true sprawi że obraz zostanie zeskalowany tak aby się zmieścił w docelowym prostokącie, nic z obrazu nie zostało obcięte oraz bez naruszenia proporcji, to przeciążenie świetnie się nadaje do generowania miniaturek (ponieważ takie warunki muszą one zazwyczaj spełnić.
Na koniec dodam że moim celem nie jest opisywanie na łamach tego bloga wszystkich elementów EmguCV – nie chcę powielać dokumentacji.
Przykładowy kod wykorzystujący ww metody znajduje się Tutaj 

Amatorskie spojrzenie na EmguCv – kontrolka Image Box

W poprzednim wpisie wspomniałem o metodzie ToBitmap klasy Image pozwalającej na konwersję klasy Image na klasę System.Drawing.Bitmap, co pozwala na używanie wyniku obróbki obrazu w kontrolkach Windows Forms. Jednak taka konwersja nie należy do najszybszych operacji, po za tym powoduje zwiększenie zużycia pamięci, ponieważ obraz jest przechowywany w pamięci w dwóch postaciach. Ponieważ najczęstszym zastosowaniem obrazów jest ich wyświetlanie do EmguCV została dodana kontrolka ImageBox.

Jest ona pod pewnymi względami podobna do PictureBox z WindowsForms, podstawową różnicą jest fakt iż jako źródła używa klasy IImage (po którym dziedziczy Image<TColor, TDepth>) , a nie Bitmap. Kontrolka ta dodaje też kilka funkcjonalności np. skalowanie obrazka myszką, obsługę rolki itp. jednak mam nadzieję że da się to wyłączyć Edit.

Aby dodać kontrolkę ImageBox do projektu należy dodać referencję do EmguCV (operację tą opisałem w poprzednim poście) i to wystarczy aby używać tej kontrolki z poziomu kodu. Jednak aby ww. kontrolka pojawiła się także w toolboxie należy się jeszcze trochę wysilić.

Po kliknięciu prawym przyciskiem myszy w wolny obszar na toolboxie należy wybrać“Add Tab” ja swoją zakładkę nazwałem “EmguControls”:

AddTab

Następnie należy kliknąć prawym przyciskiem myszy w nową zakładkę wybrać opcję “Choose Items”.

Choose Items

Na wyświetlonym oknie można należy wybrać przycisk “Browse”, wskazać ścieżkę do EmguCV.UI.dll i zatwierdzamy obydwa formularze.

Browse

Oprócz kontrolki ImageBox zostają też dodane też kontrolki HistogramBox, MatrixBox, PanAndZoomPictureBox ich eksploracją zajmę się w późniejszym czasie (o ile mi starczy chęci).

Na koniec dodam iż eksplorację EmguCV będę dodatkowo dokumentował na GitHubie w projekcie https://github.com/szogun1987/EmguExperriments

W projekcie brakuje bibliotek OpenCV ze względu na ich rozmiar, należy je sobie samemu podlinkować/skopiować.

Ponieważ projekt ma za zadanie ukazać możliwości API w jak najprostrzy sposób większość kodu będzie się znajdowała w EventHandlerach (nie znam dobrego polskiego odpowiednika tego słowa), a sam kod będzie pełen powtórzeń. Więc nie uznaję krytyki tych aspektów. Natomiast chętnie zapoznam się z innymi uwagami.

Edit:

Właściwością kontrolki determinującą zachowanie kontrolki jest FunctionalMode. Właściwość ta może przyjmować 4 wartości:

  • Minimum – ogranicza się do wyświetlenia obrazka ta na której mi zależy.
  • RightClickMenu – wyświetla menu pozwalające na dokonywanie przekształceń na obrazie, ponieważ Menu pokrywa większość funkcjonalności projektu EmguExperiments wyłącze je w swoim projekcie
  • PanAndZoom – obsługa gestów myszy
  • Everything – jak sama nazwa wskazuje wszystkie funkcjonalności – wartość domyślna dla właściwości FunctionalMode

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<Bgr, byte> image = Image<Bgr, byte>.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&oacute;w
var image2 = image.Convert&lt;Hsv, byte&gt;();
// Konwersja na skalę szarości - nieodwracalna
var image3 = image.Convert&lt;Gray, byte&gt;();

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.

Eclipse Database Development Incorrect syntax w poprawnym skrypcie

Eclipse Database Development Tools to całkiem fajne narzędzie do współpracy z SQL-owymi bazami danych. Posiada kolorwanie składni, możliwość edycji danych w tabelkach, jest zintegrowane z Eclipsem, działa z każdym silnikiem SQL z jakiego kiedykolwiek korzystałem, a co najważniejsze jest dostępne za free.

Jednak podczas przenoszenia bloga na nowy hosting i domenę przytrafiła mi się przykre doświadczenie. Próbowałem z tego narzędzia odpalić skrypt migrujący dane. I zamiast pełnej bazy zobaczyłem komunikat o niepoprawnej składni.

Okazuje się że przy odpaleniu dłuższych skryptów narzędzie to najpierw robi paskudne dzielenie skryptu po średnikach nie patrząc gdzie one się znajdują a następnie uruchamia skrypt po skrypcie.

Ponieważ mój Blog jest programistyczny napisy zawierały spore ilości średników. Skończyło się na zassaniu skryptu przez konsolę. 

Lazy Yield Problems

Zanim przejdę do sedna sprawy nakreślę najpierw ciąg wydarzeń który mnie ku napisaniu tego posta skłonił.

Ostatnimi czasy wykonywałem małą biblioteczkę "na własne potrzeby" której podstawą był interfejs który można przedstawić następująco:

public interface IProcessor&lt;T&gt;
{
IEnumerable&lt;Result&gt; Process(T input);
}

Interfejs ten ma wiele implementacji jednak z punktu widzenia tego postu kluczowy jest "AggregateProcessor" który łączy wyniki kilku procesorów.

class AggregateProcessor&lt;T&gt; : IProcessor&lt;T&gt;
{
private IProcessor&lt;T&gt;[] subProcessors;
public AggregateProcessor(params IProcessor&lt;T&gt;[] subProcessors)
{
this.subProcessors = subProcessors;
}
public IEnumerable&lt;Result&gt; Process(T input)
{
return subProcessors.SelectMany(p =&gt; p.Process(input));
/* ekwiwalent
foreach (var processor in subProcessors)
{
foreach (var result in processor.Process(input))
{
yield return result;
}
}
*/
}
}

Następnie dla tej klasy napisałem prosty test sprawdzający czy podprocesor zostanie wywołany po wywołaniu Process na AggregateProcessorze.

Test jest napisany w NUnicie do Mockowania wykorzystałem FakeItEasy.

[Test]
public void CheckIfSubProcessorIsCalled()
{
// Given
var parameter = new TestClass();
var fakeProcessor = A.Fake&lt;IProcessor&lt;TestClass&gt;&gt;();
A.CallTo(() =&gt; fakeProcessor.Process(parameter)).Returns(Enumerable.Empty&lt;Result&gt;());
var aggregate = new AggregateProcessor&lt;TestClass&gt;(fakeProcessor);
// When
aggregate.Process(parameter);
// Then
A.CallTo(() =&gt; fakeProcessor.Process(parameter)).MustHaveHappened();
}

Wielkie było moje zdziwienie gdy okazało się że Test nie przechodzi.

Przyczyną tej sytuacji jest fakt iż zarówno operacje w zapytaniu LINQ jak i operacja yield wywoływane są w momencie iteracji po wyniku metody której w tym przykładzie nie ma. W większości przypadków fakt ten nie ma większego znaczenia ponieważ iteracja zazwyczaj w końcu następuje, chyba że tak jak w tym przypadku metody będącej funkcją używamy jak metody będącej procedurą, albo wykonujemy na wyniku operacje typu Any, First/FirstOrDefault które nie iterują po całym wyniku.

Wracając do mojego przypadku ostatecznie stwierdziłem że fakt wywołania metody wewnętrznej nie jest dla mnie istotny, a bardziej obchodzi mnie zwrócenie wyniku procesora wewnętrznego.

[Test]
public void CheckIfSubProcessorIsCalled()
{
// Given
var parameter = new TestClass();
var result = new Result();
var fakeProcessor = A.Fake&lt;IProcessor&lt;TestClass&gt;&gt;();
A.CallTo(() =&gt; fakeProcessor.Process(parameter)).Returns(new[] { result });
var aggregate = new AggregateProcessor&lt;TestClass&gt;(fakeProcessor);
// When
var resultCollection = aggregate.Process(parameter);
// Then
CollectionAssert.Contains(resultCollection, result);
}

Drugim zagrożeniem jakie wiąże się z yield i LINQ jest fakt iż wszystkie operacje wykonywane pod spodem wykonują się przy każdej iteracji po wynikowej kolekcji. Sam się na tym nigdy nie przejechałem ponieważ przed błędem tego typu skutecznie chroni mnie Resharper, jednak ludzi nie posiadających tego genialnego narzędzia warto ostrzec że poniższy kod:

static void Main(string[] args)
{
var sourceCollection = Enumerable.Range(1, 10);
var resultCollection = from number in sourceCollection
select Compute(number);
foreach (var number in resultCollection)
{
Console.Write(&quot;{0}, &quot;, number);
}
Console.WriteLine();
foreach (var number in resultCollection)
{
Console.Write(&quot;{0}, &quot;, number);
}
}
private static int Compute(int parameter)
{
// Some Heavy operation
Thread.Sleep(1000);
return parameter + 1;
}

będzie uruchamiał się 20 a nie 10 sekund. Jakimś obejściem tego problemu jest użycie metody ToList. Najlepiej w następujący sposób:

static void Main(string[] args)
{
var sourceCollection = Enumerable.Range(1, 10);
var resultCollection = from number in sourceCollection
select Compute(number);
/*
* Przed wywołaniem ToList upewniam się że kolekcja nie jest już listą
* W tym przypadku nie ma to sensu ponieważ wiem że kolekcja nie jest listą
*/
var resultList = resultCollection as List&lt;int&gt; ?? resultCollection.ToList();
foreach (var number in resultList)
{
Console.Write(&quot;{0}, &quot;, number);
}
Console.WriteLine();
foreach (var number in resultList)
{
Console.Write(&quot;{0}, &quot;, number);
}
}

Podsumowując zarówno LINQ jak i yield są bardzo fajnymi narzędziami, przed ich użyciem należy jednak przynajmniej w minimalnym stopniu dowiedzieć się jak one działają.

Świąteczne małpki

W ostatnim czasie w pracy spotkałem się z problemem który w uproszczeniu wygląda następująco:

Mamy listę obiektów typu Dział zawierających kolekcje obiektów typu Wydział zawierających kolekcję obiektów typu pracownik. Problemem do rozwiązania jest policzenie średniej Wieku wszystkich pracowników. Zakładamy dodatkowo że baza nie jest tuż za płotem tylko znajduje się za WebServicem do którego dokładanie metod jest z jakiś przyczyn problematyczne, więc nie możemy po prostu złożyć składnego selecta.

Problem wydaje się być banalny wystarczy zmontować coś takiego:

int ilosc = 0;
int suma = 0;
foreach (Dzial dzial in dzialy)
{
foreach (Wydzial wydzial in dzial.Wydzialy)
{
foreach (Pracownik pracownik in wydzial.Pracownicy)
{
ilosc++;
suma += pracownik.Wiek;
}
}
}
double wynik = ((float)suma) / ilosc;

Jest to jednak kod mało czytelny. A jeżeli operacja byłaby bardziej złożona niż średnia robi się jeszcze mniej ciekawie.

Rozwiązaniem jakie narzuca się bardziej doświadczonym programistom .Net jest użycie LINQ. Rozwiązanie to ma jednak wadę: nie chodzi na kolekcjach kolekcji tylko na jednowarstwowych kolekcjach. Można ten problem rozwiązać przez wyliczenie sobie pomocniczej kolekcji i na niej wykonania działania:

List&lt;Pracownik&gt; pracownicy = new List&lt;Pracownik&gt;();
foreach (Dzial dzial in dzialy)
{
foreach (Wydzial wydzial in dzial.Wydzialy)
{
foreach (Pracownik pracownik in wydzial.Pracownicy)
{
pracownicy.Add(pracownik);
}
}
}
double wynik = pracownicy.Average(p =&gt; p.Wiek);

Jest to rozwiązanie dobre jednak tworzenie i napełnianie listy w przypadku dużych kolekcji może okazać się zasobożerne.

Dlatego też postanowiłem zrobić świąteczny prezent sobie i innym programistom i stworzyć małpkę która za nas będzie biegała po drzewach i strukturach drzewo-podobnych. W ten sposób powstało rozszerzenie ExtractSubCollection biblioteki Szogun1987.Monkey. Po wykorzystaniu tej metody powyższy kod zostaje skrócony do 4 w miarę czytelnych linijek:

double result = 
dzialy.ExtractSubCollection(d =&gt; d.Wydzialy)
.ExtractSubCollection(w =&gt; w.Pracownicy)
.Average(p =&gt; p.Wiek);

W bibliotece znajdują się dodatkowo rozszerzenia GetAllSubNodes, GetLeafs i GetRoot ułatwiające przeglądanie zwykłych (tj. składających się z węzłów spójnych pod względem typu) Drzew.

Najprostszą z nich jest GetRoot która jako parametr pobiera obiekt – węzeł drzewa i Funkcję zwracającą element nadrzędny węzła. Jako korzeń uznawany jest element dla którego powyższa funkcja zwróci null.

Kolejną metodą jest GetAllSubNodes pozwala ona na przeiterowanie po wszystkich węzłach znajdujących się poniżej węzła na którym wywoływane jest rozszerzenie (łącznie z nim samym). Jako drugi parametr przyjmowana jest funkcja zwracająca kolekcję elementów podrzędnych.

Ostatnią metodą w bibliotece jest GetLeafs pozwalająca przeiterować się po liściach drzewa z niego się wywodzących.

Dodatkowo w bibliotece znajduje się interfejs ITreeNode którego zaimplementowanie sprawia że do funkcji GetAllSubNodes, GetLeafs i GetRoot nie trzeba przekazywać funkcji wyciągających "najbliższych członków rodziny" danego elementu.

Dołożyłem starań aby biblioteka była napisana w sposób powodujący jak najniższe zużycie zasobów systemowych (pamięci i procesora). Podobnie jak w LINQ wszelkie funkcje i metody przekazywane do moich rozszerzeń są wywoływane w momencie iteracji.

Na koniec kwestie prawne, (Co by było wszystko na papierze znaczy się monitorze):

Zezwalam na nieodpłatne korzystanie ze skompilowanej wersji biblioteki. Biorę na siebie poprawki ewentualnie wykrytych błędów, przy czym nie naprawiam błędów wynikających ze współbieżnego dostępu do drzewa. Nie gwarantuję także wprowadzania ulepszeń.

Kodu źródłowego nie udostępnię w tym roku. Zakładam taką możliwość w pierwszym kwartale roku przyszłego (2012).

Biblioteki są do pobrania Tutaj

Problem zwrotnych referencji w WCF

Podczas wielu batalii z WCF'em natknąłem się na konieczność umieszczenia w obiekcie będącej elementem jakiejś kolekcji referencji do obiektu posiadającego tą kolekcję. (Przyznam że konieczność ta była związana z faktem iż w przesyłanej klasie była zaszyta część logiki biznesowej a tak się nie powinno robić ale jak mus to mus). Tak więc posiadałem DataContract'y podobne do tych:

[DataContract]
public class Parent
{
[DataMember]
public string Name { get; set; }
[DataMember]
public List&lt;Children&gt; Childrens { get; set; }
}
[DataContract]
public class Children
{
[DataMember]
public string Name { get; set; }
[DataMember]
public Parent Parent { get; set; }
}

Próba pobrania obiektu Parent zakończyła się błędem:

WCFBlad1.png

Znalezienie przyczyny błędu było na tyle trudniejsze że pomiędzy buildem a napisaniem DataContractu robiłem całą masę innych rzeczy.

Błąd ten jest spowodowany wyjątkiem StackOverflow po stronie serwera, wynika on z faktu że przy próbie serializacji klasy Children mechanizm docierając do pola Parent nie orientuje się że już ten obiekt serializował albo właśnie go serializuje.

Rozwiązaniem tego problemu jakie znalazłem googlając było ustawienie właściwości IsReference w klasie rodzica i rzeczywiście w tak prostym przypadku rozwiązanie to działa jak należy.

[DataContract(IsReference=true)]
public class Children
{
[DataMember]
public string Name { get; set; }
[DataMember]
public Parent Parent { get; set; }
}

Moja sytuacja była jednak trochę bardziej złożona. Po pierwsze zarówno Parent jak i Children dziedziczyło po jednym typie bazowym:

[KnownType(typeof(ChildrenBP))]
[KnownType(typeof(ParentBP))]
[DataContract]
public class SuperBP
{
[DataMember]
public string Name { get; set; }
}
[DataContract(IsReference=true)]
public class ParentBP : SuperBP
{
[DataMember]
public List&lt;ChildrenBP&gt; Childrens { get; set; }
}
[DataContract]
public class ChildrenBP : SuperBP
{
[DataMember]
public ParentBP Parent { get; set; }
}

Ustawienie znacznika IsReference na klasie ParentBP i pobranie referencji kończy się błędem:

pictures/WCFBlad2.png

W większości przypadków należy postąpić zgodnie z komunikatem błędu i ustawić IsReference na klasie bazowej. Jeżeli jednak z jakiś powodów nie chcemy tego robić możemy usunąć znacznik DataMember z pola Parent w klasie ChildrenBP i posiłkować się znacznikiem OnDeSerialized.

[KnownType(typeof(ChildrenBP))]
[KnownType(typeof(ParentBP))]
[DataContract]
public class SuperBP
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class ParentBP : SuperBP
{
[OnDeserialized]
public void OnDeserialized(StreamingContext context)
{
foreach (ChildrenBP child in Childrens)
{
child.Parent = this;
}
}
[DataMember]
public List&lt;ChildrenBP&gt; Childrens { get; set; }
}
[DataContract]
public class ChildrenBP : SuperBP
{
public ParentBP Parent { get; set; }
}

Słów kilka o krotkach.

Chciałem dzisiaj opisać trochę moje doświadczenia z krotkami.

Pomimo że ich konstrukcja nie jest złożona, a istnienie klasy o ich cechać było wyczekiwane dość długo to pojawiły się one w .Net dopiero w wersji 4.0, a ich pojawienie się zostało zdecydowanie zagłuszone przez inne nowinki w tej wersji.

Pierwszym narzucającym się zastosowaniem dla krotek jest wielotypowana tablica. Tablica taka przydaje się gdy musimy przerzucić listę kilkuelementowych paczek obiektów pomiędzy dwoma miejscami w kodzie. Przykładem takiego miejsca wywoływanie operacji na bazie dla każdego rekordu zwróconego przez obiekt implementujące IDataReader. Jak wiadomo przy próbie wywołania zapytania przed zamknięciem Readera następuje zgłoszenie wyjątku. Problem taki rozwiązywało się na kilka sposobów (oczywiście nie polegających na rezygnowaniu z Readera):

1) Utworzenie kilku list dla każdego przepisywanego pola – powoduje konieczność stosowania pętli for zamiast foreach

2) Utworzenie listy tablic obiektów (listy słowników, lub innej wariacji generycznej) – fajne rozwiązanie dopuki nie potrzebna nam informacja o rzeczywistym typie elementu (wtedy trzeba rzutować).

3) Utworzenie klasy i przesyłanie listy obiektów tej klasy – C# to nie Java tutaj duża lisczba klas kuje w oczy ;]

4) Klasy anonimowe – byłyby idealne gdyby nie problem z nacechowaniem ich typem listy

5) Stosowanie elementu klasy stworzonej w innym celu (np. KeyValuePair<T1, T2>)

Zastosowanie krotek jest rozwiązaniem łączącym znaczną część zalet powyższych rozwiązań jednocześnie nie posiada większości ich wad:

List&lt;Tuple&lt;int, string&gt;&gt; tempList = new List&lt;Tuple&lt;int, string&gt;&gt;();
using(SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = &quot;select id, nazwa from Tabela1&quot;;
using(IDataReader reader = cmd.ExecuteReader())
{
while(reader.Read())
{
tempList.Add(new Tuple&lt;int, string&gt;());
}
}
}
using(SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = &quot;insert into Tabela2(id, nazwa) values (@id, @nazwa)&quot;;
foreach(var tuple in tempList)
{
cmd.AddWithValue(&quot;@id&quot;, tuple.Item1);
cmd.AddWithValue(&quot;@nazwa&quot;, tuple.Item2);
cmd.ExecuteNonQuery();
}
}

Inną ciekawostką związaną z krotkami jest fakt iż mają solidnie zaprogramowane funkcje GetHashCode i Equals, przez co można je wykorzystać żeby ułatwić sobie implementację tych metod:

class ExampleClass
{
public int IdPart1 { get; set; }
public int IdPart2 { get; set; }
public override int GetHashCode()
{
return new Tuple&lt;int, int&gt;(IdPart1, IdPart2).GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is ExampleClass)
{
ExampleClass other = (ExampleClass)obj;
return new Tuple&lt;int, int&gt;(IdPart1, IdPart2).Equals(new Tuple&lt;int, int&gt;(other.IdPart1, other.IdPart2));
}
return false;
}
}

albo z wykorzystaniem LambdaEqualityComparer

class ExampleClass
{
public int IdPart1 { get; set; }
public int IdPart2 { get; set; }
private LambdaEqualityComparer&lt;ExampleClass, Tuple&lt;int, int&gt;&gt; comparer = 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new LambdaEqualityComparer&lt;ExampleClass, Tuple&lt;int, int&gt;&gt;(ec =&gt; new Tuple&lt;int, int&gt;(ec.IdPart1, ec.IdPart2));
public override int GetHashCode()
{
return comparer.GetHashCode(this);
}
public override bool Equals(object obj)
{
return comparer.Equals(this, obj as ExampleClass);
}
}

Krotki implementują też interfejs IComparable co może być przydatne jeżeli chcielibyśmy zaimplementować ten interfejs w swoich klasach (prawie klasyczny przykład Dekoratora).

Kolejnymi interfejsami implementowanymi przez Krotki są IStructuralEquatable i IStructuralComparable jest to związane z silnym wykorzystywaniem ich w języku F# (niestety jeszcze do niego nie przysiadłem). Więcej informacji na ten temat można uzyskać od Marcina Najdera z firmy Comarch który silnie promuje ten język na konferencjach.

Ze względu na fakt iż krotki po zainicjowaniu nie zmieniają swojego stanu mają podobno zastosowanie w aplikacjach współbieżnych (podobno bo sam nie miałem okazji się o tym przekonać).

Na koniec chciałbym dodać kilka słów o wykrytych przeze mnie niedopracowaniach krotek.

Pierwsza niedogodność Nazwy Item1, Item2, Item3… często uniemożliwiają domyślenie się co jest co, jednak po pierwsze nie mam innego pomysłu na nazwy tych właściwości, po drugie z założenia powinny być one wykorzystywane jako tymczasowe pojemniki na obiekty.

Druga fakt iż nie da się ich łatwo zserializować i deserializować (co też nie jest ich poprawnym zastosowaniem).

W komentarzach proszę o umieszczanie pomysłów na inne zastosowanie dla krotek.

Wrażenia po wizycie na 4Developers

4 kwietnia odbyła się w Warszawie konferencja 4 Developers. Z racji że uczestniczyłem w niej w poprzednich latach postanowiłem że nie opuszczę jej także w tym roku.

Monika Konieczny – Gramification

Ciekawy temat, niezbyt ciekawe wykonanie. Wykład dotyczył kolejnej metody motywacji pracowników, w tym programistów. Metoda ta polega na wprowadzeniu elementów gry podczas realizacji projektu, tak aby pracownicy czerpali radość z pracy. Jako przykład podany zostało rozgrywanie jakiejś gry w określonych odstępach czasu przy czym warunki początkowe gracza zależą od jego tygodniowych wyników (np. liczba kulek do Paintboala rozdawanych w comiesięcznej rozgrywce zależy od części projektu jaki wykonał pracownik). Według prelentki powinno się jednak szybko z gier indywidualnych przechodzić do zespołowych. Ogólnie prezentację oceniam na dobry minus.

Greg Young – zapomnij o szczegółach liczy się efekt

O autorze tej prezentacji przeczytałem na blogu Sławka Sobótki. Początkowo sądziłem że Sławek przesadza z mówiąc że wysłuchanie Grega powoduje efekt "I know kung-fu". Jednak później się przekonałem, że ma rację. Prelekcja była skierowana głównie do programistów Java, dlatego skrytykował on na samym początku używanie potężnych Framewoków i bibliotek w projektach w których samo ich skonfigurowanie zajmuje więcej czasu niż wykonanie projektu, dodatkowo powodując całą masę dziwnych błędów. Jak to określił "Frameworks are DSLs to converting XML into exceptions". Przez dłuższy czas było precyzowane stwierdzenie że klasa rozwiązań powinna być dobrana do klasy problemu. Że zupełnie inne rozwiązania powinno się stosować pisząc pamiętnik dla młodszej siostry, a inne tworząc aplikację nadzorującą działanie układu chłodzącego rdzeń elektrowni atomowej. Greg poruszył też zagadnienie zaciągania "technologicznego długu" podczas pracy nad projektem. Zastosował on sformuowania "Dobry dług" i "Zły dług" zaczerpnięte z publikacji Roberta Kiyosaki. Jedna z wypowiedzi mówiła o zasadności (nie)stosowania testów jednostkowych w projektach które będą rozwijane nie dłużej niż 2-5 lat. Według programistów pisanie testów jest dobrym zachowaniem, jednak wg. kadry zarządzającej powoduje całą masę pracy, która kosztuje i zmniejsza dochodowość projektu. Greg bardzo trafniej porównał stosowanie Testów jednostkowych do wzięcia na kredyt Lamborgini. Dla osób dla których kluczowym elementem sukcesu jest wizerunek (np. gwiazdy sportu i filmowe, prezesi dużych przedsiębiorstw) jest to z pewnością inwestycja("Dobry dług"), jednak dla przeciętnego zjadacza chleba koszty kredytu i utrzymania samochodu tej klasy przewyższają zarówno potencjalny wzrost zarobków a nawet same zarobki("zły dług") i analogicznie stosowanie testów jednostkowych ma sens w systemie bankowym gdzie pomyłka w algorytmie naliczania odsetek może kosztować zarówno bank jak i producenta oprogramowania miliony złotych, jednak mija się z celem w aplikacji napisanej dla sklepu ze skarpetkami gdzie na upartego można w ciągu jednej nocy wprowadzić ponownie wszystkie wystawione i odebrane faktury z ostatniego miesiąca. Kolejną kwestią poruszoną przez Grega była kwestia wzorców projektowych i sensu ich stosowania. Skrytykował on podejście w którym na siłę próbuje się stosować wzorce projektowe (tylko po to by je stosować). Podkreślił jednak ich rolę w komunikacji między proramistami (podając jako przykład Fabrykę Fabryk Fabryk której opisanie bez znajomości wzorców zajęłoby więcej niż ten wpis na blogu).

Ciekawostką jest fakt iż Greg nie posiadał przygotowanej prezentacji także nawet nie włączył projektora.

Michał Gruchała – Architektura skalowalnych systemów Webowych

Prezentacja dotyczyła zagadnienia optymalizacji prostych serwisów poddawanych bardzo dużemu obciążeniu (rzędu kilku milionów wejść na godzinę). Z racji że nie zajmuje się tym zagadnieniem nie specjalnie słuchałem prelegenta.

Sławomir Sobótka – Nowe bardziej racjonalne podejście do warstw

Prezentacja Sławka dotyczyła CQRS (Command Query Responsibility Segregation) która jest rozwinięciem Domain Driven Design. Podejście to zaleca oddzielenie serwisu zwracającego dane od serwisu je zmieniającego, co w dużym uproszczeniu oznacza rezygnację ze zwracania jakiś wartości przez metody które dokonują jakichkolwiek zmian w składowanych danych, oraz rezygnację z modyfikowania danych w metodach dane zwracające. Sławek podobnie jak Greg zwracał uwagę na zastosowanie na dostosowanie rozwiązania do problemu oraz że Domain Driver Design ma zastosowanie głównie przy pisaniu złożonych aplikacji biznesowych, z resztą wyraźnie widać było że Sławek przesiąkł myślą techniczną Greg'a. Nie czuję się na siłach aby przekazać ideę całego wykładu ponieważ sam muszę ją zgłębić.

Stephan Hochdöerfer – DDD jeszcze raz ale inaczej

Podobnie jak pierwsza prelekcja Ciekawy temat nieciekawe wykonanie.

Greg Young – Uwolnij swoją domenę

Wykład ten można potraktować jako rozwinięcie wcześniejszego wykładu Grega i wykładu Sławka Sobótki. Podobnie jak w przykładzie prezentacji Sławka nie podejmuje się analizy tego wystąpienia.

Ogólnie konferencję oceniam bardzo dobrze, a napewno lepiej niż zeszłoroczną. Lepiej dobrano lokalizację (Hotel w Poznaniu śmierdział według mnie komuną, a budynek Wyższej Szkoły Menagerskiej w Warszawie nawet jeżeli nie był nowy to był bardzo dobrze odnowiony), do zalet należy też zaliczyć fakt iż w tym roku posiłek był wliczony w koszt konferencji, oraz że nie zabrakło przystawek. Do wad lokalizacji należy zaliczyć dużą odległość od centrum Warszawy oraz brak ścieżki .Net oraz przedstawicieli firm Adobe, Microsoft i Oracle (może i byli ale ich widać nie było).

Komunikacja PHP i C Sharp

Ostatnio musiałem napisać aplikację w której aplikacja .Net współpracuje z aplikacją napisaną w PHP za pośrednictwem gołego http.

Sprawa nie jest szczególnie złożona jednak jak się później okazało nie jest taka prosta jak myślałem. Fragmenty kodu zawarte w Blogu pochodzą z przykładowego programu dostępnego do pobrania na końcu postu.

To co było proste:

Komunikacja PHP z dowolną inną platformą odbywa się za pomocą protokołu HTTP który jest ogólnie dobrze opisany np. tutaj. Dodatkowo w większości platform istnieją dedykowane biblioteki obsługujące w mniejszym lub większym stopniu komunikację przez http, a biblioteka standardowa .Net nie jest pod tym względem wyjątkiem(tfu!).

Komunikację za pomocą http ułatwiają przynajmniej dwie klasy HttpWebRequest i WebClient. Klasa WebClient ma w moim mniemaniu znacznie bardziej intuicyjny interfejs i to jej będę się starał używać.

Wywołanie GET:

Wywołanie GET jest odwołaniem się do strony www równoważnym z wpisaniem adresu w przeglądarce internetowej, parametry do serwera wysyła się dopisując po adresie strony znak zapytania i wartości tych parametrów w formacie parametr1=wartosc1&parametr2=wartosc2&parametr3=wartosc3. Ograniczeniem tej metody jest fakt iż długość adresu strony wraz z wartościami parametrów nie może przekraczać 256znaków.

Metody tej nie powinno się także stosować na stronach w celu przekazywania danych wrażliwych (jak np. login i hasło użytkownika).

Do zbudowania łańcucha połączeniowego można użyć klasy UriBuilder, jest to klasa która na podstawie fragmentów adresu potrafi złożyć pełną ścieżkę do pliku/katalogu także na serwerze www.

Klasą która się przydaje jest także HttpUtility posiadająca metodę UrlEncode zwalniającą programistę z wstawiania odpowiednich znaków ucieczki w wysyłanym zapytaniu do serwera, niestety klasa ta znajduje się w przestrzeni nazw System.Web w bibliotece o tej samej nazwie, biblioteka ta zaś nie znajduje się w Wersji Client Profile platformy .Net. Tak więc jeżeli klasy tej chcemy użyć w projekcie Windows Forms lub WPF musimy dokonać następujących zmian w projekcie:

1) Zapisanie wszystkich niezapisanych plików.

2) W Solution Explorze klikamy prawym przyciskiem myszy na projekt i wybieramy Properties

3) W zakładce Application znajdujemy Combobox Target platform i wybieramy w nim .Net Framework 4.0

4) Zapisujemy wszysto i zamykamy właściwości projektu

5) Klikamy prawym przyciskiem w katalog refereces w projekcie i wybieramy AddReference

6) Przechodzimy na zakładkę .Net i wybieramy System.Web

7) Zatwierdzamy dialog i możemy używać klasy System.Web.HttpUtility

Najprostszy kod pobierający treść strony www może wyglądać tak.

WebClient client = new WebClient();
// Przy niekt&oacute;rych serwerach aplikacja powinna się przedstawić 
//client.Headers.Add(&quot;user-agent&quot;, &quot;PHP and dotNet&quot;);
UriBuilder queryBuilder = new UriBuilder(tbServer.Text + &quot;helloGet.php&quot;);
queryBuilder.Query = string.Format(&quot;name={0}&quot;, HttpUtility.UrlEncode(tbName.Text));
string result = client.DownloadString(queryBuilder.Uri);
MessageBox.Show(this, result);

plik helloGet.php zawiera chyba najprostrzą możliwą obsługę zapytania GET:

&lt;?php
echo &#039;Witaj &#039;.$_GET[&#039;name&#039;];
?&gt;

Pobranie plików z serwera:

Zadanie trochę mniej związane z PHP, aczkolwiek pojawiające się w pracy z http to pobranie pliku z serwera http. Tutaj klasa WebClient oferuje dwie możliwości.

Metoda DownloadFile podaje się adres pliku i nazwę pliku nic prostszego

using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = &quot;Pliki jpeg|*.jpg&quot;;
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
WebClient client = new WebClient();
client.DownloadFile(&quot;http://www.programy.witraze-plocin.pl/wp-content/uploads/2010/04/cycki226jo5.jpg&quot;, sfd.FileName);
}
}

Metoda DownloadData zwraca tablicę bajtów składających się na plik metoda może być przydatna gdy pobrane dane chcemy tylko obrobić bez zapisywania ich na dysku. Np. wyświetlić obrazek w pictureBoxie.

WebClient client = new WebClient();
byte[] buffer = client.DownloadData(&quot;http://www.programy.witraze-plocin.pl/wp-content/uploads/2010/04/cycki226jo5.jpg&quot;);
MemoryStream stream = new MemoryStream(buffer);
// Kwestie techniczne
if (pbCycki.Image != null)
{
Image oldImage = pbCycki.Image;
pbCycki.Image = null;
oldImage.Dispose();
}
pbCycki.Image = Image.FromStream(stream);

Wadą tej metody jest spore zajęcie pamięci ponieważ cały plik jest wgrywany do RAMu, dodatkowo aplikacja musi pobrać cały plik i dopiero wtedy go obrobić. Przypadkiem gdy żadna z powyższych metod nie zastosowania jest pobranie dużego pliku z serwera i zapisanie go do strumienia nie będącego plikiem(np. wysłać portem COM do jakiegoś urządzenia). W tym przypadku należy wykorzystać metodę WebRequest.

private void FromHttpToStream(Stream outStream, string url)
{
WebClient client = new WebClient();
client.OpenRead(url).CopyTo(outStream);
}

Wywołanie POST:

Wysłanie zapytania POST do serwera http przy pomocy klasy HttpClient jest dosyć niskopoziomowe. Ponieważ należy wykonać następujące kroki:

1) Dodać do nagłówka wiadomości informację o rodzaju przesyłanej treści "content-type: application/x-www-form-urlencoded"

2) Sformatować treść wysyłanej wiadomości przy UrlEncode

3) Zakodować informację przy pomocy kodowania ASCII i wysłać na serwer.

4) Rozkodować przesłaną wiadomość przy pomocy kodowania użytego na stronie.

WebClient client = new WebClient();
// Przy niekt&oacute;rych serwerach aplikacja powinna się przedstawić 
//client.Headers.Add(&quot;user-agent&quot;, &quot;PHP and dotNet&quot;);
client.Headers.Add(&quot;Content-Type&quot;, &quot;application/x-www-form-urlencoded&quot;);
byte[] buffer = Encoding.ASCII.GetBytes(string.Format(&quot;name={0}&quot;, HttpUtility.UrlEncode(tbName.Text)));
buffer = client.UploadData(tbServer.Text + &quot;helloPost.php&quot;, &quot;POST&quot;, buffer);
string result = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
MessageBox.Show(this, result);

Upload plików:

Upload plików też jest dość prosty klasa WebClient posiada metodę UploadFile, która jako parametry pobiera nazwę wysyłanego pliku i adres pliku do którego wysyłana jest zawartość pliku.

using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = &quot;Pliki jpeg|*jpg&quot;;
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
WebClient client = new WebClient();
client.UploadFile(tbServer.Text + &quot;uploadFile.php&quot;, ofd.FileName);
}
}

Po stronie serwera wysyłany plik jest opisany w tabeli $_FILES w polu 'file' obsługa pliku. Przykładowa obsługa może wyglądać następująco:

&lt;?php
$tempFile = $_FILES[&#039;file&#039;][&#039;tmp_name&#039;];
copy($tempFile, &#039;uploaded.jpg&#039;);
unlink($tempFile);
?&gt;

Pliki z kodem przykładowego programu są do pobrania tutaj.