mg_pyopt

Aby rozpocząć optylamlizowanie programu, na początek warto zrobić profilowanie programu, aby dowiedzieć się jakie funkcje wywoływane są najcześciej, oraz które obliczenia trwają najdłużej. Profilowanie jest bardzo proste do wykonania wystarczy stworzyć plik, w którym umieszczamy następujące instrukcje: code format="python" import profile  #importujemy moduł profilowania import detekcjaRuchu #importujemy naszą klasę dR_m = detekcjaRuchu.MotionTrackker #tworzymy obiekt klasy profile.run("dR_m.test") #metoda którą profilujemy code Metoda test służy wyłącznie do testów i polega na uruchomieniu 100 razy głównej metody, czyli przetworzenie 100 klatek. Profilowanie wykazało, że dużo zasobów procesora posięcane na operacje na listach, oraz obliczanie średniego koloru regionu klatki. Listy zamieniłem na macierze z pakiety [|NumPy], które są mocno zoptymalizowane. Okazało się, że listy też są mocno zoptymalizowane i zamiana na macierze przyspieszyła program o 5%.

Okazało się iż przeoczyłem metodę zwracającą indeks dla określonych wartości macierzy. Funkcja argmax działa w ten sposób, że zwraca indeks wartości z "rozpłaszczonej" macierzy, dla przykładu jeżeli mamy macierz o wymiarach 10x10 to funkcja argmax operuje na macierzy o wymiarach 100x1, zatem jeżeli poszukiwana wartość na pozycji [9,9] (ostatnia wartość), to funkcja zwróci wartość 99. Trudniejsze byłoby znalezienie indeksu dwuwymiarowego, ale w tym przypadku jednowymiarowy w zupełności wystarcza. Użycie tej funkcji znacznie przyspiesza działanie programu, ponieważ mój sposób szukania indeksu był znacznie wolniejszy.

Następnie zająłem się optymalizacją obliczaniem średniej koloru. W swoim programie używam metody mean z modułu ImageStat, która sumuje wartości koloru pikseli oraz dzilei przez ilość pikseli. Jest to bardzo prosta funkcja która jest bardzo często wywoływana i pochłania 1/3 czasu pr acy procesora, zatem warto ją zoptymalizować. Postanowiłem użyć do tego pakietu [|Cython], dzięki któremu można przetłumaczyć kod pythona do C i stworzyć biblotekę z funkcjami działającymi w programach napisanych w pythonie, Przyjazny opis jak korzystać z tego narzędzia znajduje się pod tym [|adresem].

Kod funkcji umieszczamy w oddzielnym pliku c_mean.pyx: code format="python" def mymean(data): return sum(data)/len(data) code

następnie tworzymy plik konfiguracyjny setup.py: code format="python" from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext

setup( name = 'Image mean', ext_modules=[Extension('c_mean', ['c_mean.pyx'])], cmdclass = {'build_ext': build_ext} ):

code

na konieckompilujemy bibliotekę code format="bash" python setup.py build_ext --inplace code Tak przygotowana funkcja użyta w programie pytonowym działa dwukrotnie szybciej niż orginalna funkcja, dodtkowe użycie specjalnych typów biblioteki cython może przyspieszyć działanie funkcji o 10-15 razu. Moj program wykorzystujący funkcję przetłumaczoną do c działa o 15% szybciej.

Pewne przyspieszenie w działaniu programu uzyskałem przez zmiane sposobu wczytywania pierwszych klatek. Do uzyskanie pierwszego wyniku potrzeba trzech klatek (odległość miedzy różnicą pierwszej i drugiej klatki a różnicą drugiej i trzeciej klatki). Wcześniej sprawdzałem w każdym przebiegu przetwarzania czy dana klatka nie jest jedną z dwóch pierwszych i rysowanie zaczynało się dopiero po wczytani trzech klatek, teraz w trakcie uruchamiania programu klatki są inicjalizowane zerami, ma to pewien minus, mianowicie na początku program wykrywa ruch z lewego górnego rogu (wpływ zainicjalizowanych pustych klatek). Problem jest wynikiem tego, że przygotowywanie klatek do obliczeń wykonywane jest w tej samej funkcji co obliczenia. Jeżeli oddzieli się przygotowywanie klatek od obliczeń i zainicjalizuje klatki odpowiednimi danymi, to problem zniknie. Ta sytacja ukazuje słuszność pewnej zasady programowania, mówiącej o tym że lepiej jest mieć wiele "małych" funkcji niż jedną "dużą".