tag:blogger.com,1999:blog-197227402024-02-07T14:02:10.524+01:00Rafał PetryniakBlog techniczny o przetwarzaniu obrazów i nie tylkoRafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.comBlogger79125tag:blogger.com,1999:blog-19722740.post-40785387483970976962012-12-29T00:27:00.001+01:002012-12-29T10:19:42.206+01:00Do biegu gotowi? LavaVision!<div dir="ltr" style="text-align: left;" trbidi="on">
Kończy się rok, a ja już myślę o następnym. To nie był rok końca świata, tylko rok narodzin. W tym roku powołałem do życia moje nowe przedsięwzięcie - firmę <i><b><a href="http://www.facebook.com/lavavision" target="_blank">LavaVision</a></b></i>. Doświadczenia zdobyte w ostatnich latach w różnych projektach badawczo-rozwojowych będą teraz miały szanse wykorzystania w zastosowaniach praktycznych.<br />
<br />
Z grupą współpracujących osób planujemy przedstawić kilka innowacyjnych rozwiązań z zakresu wizji komputerowej, naturalnych interfejsów użytkownika oraz systemów komputerowego wsparcia diagnostyki obrazowej. Lubimy eksperymentować i dlatego jesteśmy otwarci na nowe pomysły. Pierwszym wyzwaniem było dla nas ożywienie miedziorytów krakowskiego artysty Krzysztofa Skórczewskiego. Efektu nie da się opisać słowami. Trzeba to było zobaczyć (i poczuć) na żywo.<br />
<br />
Jeśli chcesz być na bieżąco z naszymi działaniami zaglądaj koniecznie na nasz <a href="http://www.facebook.com/LavaVision" target="_blank">Fan Page na Facebooku</a>. W 2013 będzie się działo :-) Trzymajcie za nas kciuki!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.facebook.com/lavavision" target="_blank"><img alt="" border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZU0kwpLSDN0qXjrD0KRUWHP-7cPa4d4wtGVIkL5jBZkUbSPHqqIMC1eiFCjeZt6PWOBgPUnREeZ95Mi-ZA6Xc6haK9feitkVu7RIZQpqQZ3VEsrn0kbPvhMQ7rzdvEfnsW6NP/s400/LavaVision-logo.png" title="Śledź nasz profil na Facebooku" width="400" /></a></div>
<br />
<br /></div>
Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-82924076257262538232012-03-05T15:14:00.000+01:002012-03-05T15:14:10.455+01:00Algorytm Perona-Malik<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: right;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><span style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_627283070"><img border="0" src="http://riad.pk.edu.pl/~rpetryniak/website_resources/images/logo/github-logo1.png" /></a></span></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/My-CImg-Algorithms-Implementations" target="_blank">Pełny kod źródłowy</a></td></tr>
</tbody></table>
Jak usunąć szum z obrazu nie tracąc przy okazji cennej informacji o lokalizacji krawędzi? Odpowiedź na to pytanie można znaleźć w mojej <a href="http://rpetryniak.blogspot.com/2012/02/rozprawa-w-sieci.html" target="_blank">Rozprawie doktorskiej</a> w rozdziale <b>3.2 - Nieliniowe wygładzenie obrazu bazujące na funkcji Gaussa</b>. Rozpoczyna się on tak:<br />
<br />
<blockquote class="tr_bq">
Pomimo tego, że rozmycie funkcją Gaussa lepiej sobie radzi z zachowaniem granic między obiektami niż zastosowanie średniej arytmetycznej, to w rzeczywistości każdy punkt jest tak samo przetwarzany, niezależnie od tego czy leży wewnątrz obiektu, czy też w pobliżu jego granicy. Jest to znana cecha filtrów liniowych. Skuteczniejszym rozwiązaniem mogłoby się okazać indywidualne traktowanie każdego punktu w zależności od jego lokalizacji, tak aby proces filtracji był wzmocniony na obszarach jednolitych, a zahamowany w pobliżu krawędzi. Jedną z pierwszych prac w tym zakresie był artykuł Perona i Malika[53].<br />
<br />
[53] <b>P. Perona, J. Malik. Scale-space and edge detection using anisotropic diffusion. IEEE Transactions on Pattern Analysis and Machine Intelligence, 12(7):629–639,July 1990.</b></blockquote>
<div style="text-align: right;">
[Rozprawa doktorska, <a href="http://www.scribd.com/fullscreen/81685046?start_page=38" target="_blank">str. 28</a>]</div>
<br />
Nie będę tutaj powtarzał treści, którą można doczytać we właściwym tekście rozprawy. Chciałbym jedynie podzielić się dodatkowymi materiałami, które opracowałem przy okazji jej pisania.<br />
<br />
Najpierw chciałbym polecić rozprawę doktorską Bartosza Jabłońskiego "<i>Równania różniczkowe cząstkowe w problemach filtracji obrazów i trajektorii przestrzennych</i>" jako świetne wprowadzenie do tematyki nieliniowej filtracji obrazów. Jest ona w całości dostępna w sieci jako <a href="http://www.dbc.wroc.pl/Content/1925/Jab%C5%82o%C5%84ski_Adobe.pdf" target="_blank">plik PDF</a>. Drugi rozdział tej pracy "<i>Porównanie metod filtracji dla obrazów dwuwymiarowych</i>" to w mojej opinii najlepszy polskojęzyczny materiał na ten temat.<br />
<br />
Ja osobiście poza krótkim opisem w rozprawie jak działa algorytm Perona-Malik i porównaniem tego algorytmu do klasycznej filtracji Gaussa mogę podzielić się jeszcze jego implementacją. Opracowałem ją na podstawie książki "<i>Mathematical Problems in Image Processing</i>" (autorzy: Gilles Aubert i Pierre Kornprobst) z wykorzystaniem biblioteki <a href="http://rpetryniak.blogspot.com/2008/07/cimg_19.html" target="_blank">CImg</a>. Kod jest dostępny w <a href="https://github.com/rpetryniak/My-CImg-Algorithms-Implementations" target="_blank">moim publicznym repozytorium GitHub</a>.<br />
<br />
I jeszcze na "deser" prezentacja działania algorytmu Perona-Malik dla kilku wybranych obrazów.<br />
<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="320" src="http://www.youtube.com/embed/zJNdajktguA" width="500"></iframe>
</div>
<div style="text-align: center;">
<i><a href="http://www.youtube.com/watch_popup?v=zJNdajktguA&vq=hd720" target="_blank">Oglądaj film w rozdzielczości HD na pełnym ekranie.</a></i> </div>
</div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com7tag:blogger.com,1999:blog-19722740.post-48684579668291767882012-02-21T08:51:00.000+01:002012-03-05T11:28:30.317+01:00Rozprawa w sieci<div dir="ltr" style="text-align: left;" trbidi="on">
Przyszedł w końcu ten czas, aby podzielić się w sieci pełnym tekstem rozprawy doktorskiej, nad którą pracowałem przez ostatnie 3 lata. Rozprawa sama w sobie jeszcze nie jest "obroniona", dlatego tym bardziej zależy mi na tym, aby była ona dostępna dla każdego, kto chce przyjść na jej publiczną prezentację, która została zaplanowana na 1 marca na AGH (<a href="http://www.eaie.agh.edu.pl/~doktorant/2012:petryniak:start" target="_blank">więcej szczegółów</a>).<br />
<div>
<br /></div>
<div>
Nie będę teraz szczegółowo rozpisywał się czym zajmowałem się w trakcie prowadzonych badań i jakie wyniki udało mi się uzyskać. Zapraszam po prostu do przekartkowania tekstu, który zamieściłem poniżej.<br />
<br />
Postaram się w najbliższym czasie podzielić dodatkowymi przykładami, algorytmami i narzędziami, które zostały opracowane na potrzeby pracy, a z powodu przyjętych ograniczeń (np. nie opisywałem kodu źródłowego i skrótowo przedstawiałem powszechnie znane algorytmy) nie zostały one umieszczone w tekście właściwym.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.scribd.com/fullscreen/81685046" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://riad.usk.pk.edu.pl/~rpetryniak/research/Rafal_Petryniak-phd-cover-v1.png" title="Rafał Petryniak (wersja elektroniczna w serwisie Scribd)" /></a>
</div>
<div style="text-align: center;">
<a href="http://www.scribd.com/fullscreen/81685046" target="_blank">Rafał Petryniak (wersja elektroniczna w serwisie Scribd)</a><br />
<br />
<div style="text-align: left;">
<b>Aktualizacja</b>: 2 marca 2012<br />
<br /></div>
<div style="text-align: left;">
Poniżej zamieszczam prezentację z obrony. Pozostałe dodatkowe materiały powiązane z doktoratem znajdują się na stronie:<br />
<div style="text-align: center;">
<a href="https://sites.google.com/site/rafalpetryniakresearchpl/list-of-publications/phd_thesis"><span class="Apple-style-span" style="font-size: x-small;">http://sites.google.com/site/rafalpetryniakresearchpl/list-of-publications/phd_thesis</span></a>.</div>
<br />
<div style="text-align: center;">
<br /></div>
</div>
<div style="text-align: left;">
<div style="text-align: center;">
<iframe allowfullscreen="true" frameborder="0" height="389" mozallowfullscreen="true" src="https://docs.google.com/presentation/embed?id=1fIkgXJVLBiwL_kAEUi3ix-OeP6Sal4tpsxeYePWY6OA&start=false&loop=false&delayms=3000" webkitallowfullscreen="true" width="480"></iframe>
</div>
</div>
</div>
</div>
</div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-69292752326374618682012-01-09T22:54:00.001+01:002014-01-04T12:08:49.427+01:00Aplikacja interaktywna w OpenCV reagującą na ruch<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: right;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><span style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_627283070"><img border="0" src="http://riad.pk.edu.pl/~rpetryniak/website_resources/images/logo/github-logo1.png" /></a></span></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_5" target="_blank">Pełny kod źródłowy</a></td></tr>
</tbody></table>
Kurs programowania systemów interaktywnych z wykorzystaniem <b><i>OpenCV</i></b>, który jakiś czas temu rozpocząłem na łamach tego bloga (patrz: <a href="http://rpetryniak.blogspot.com/search/label/OpenCV" target="_blank">etykieta OpenCV</a>), byłby niekompletny, gdyby zabrakło opisu interakcji ruchowej. <a href="http://rpetryniak.blogspot.com/2011/05/opencv-obsuga-klawiatury-i-myszki.html" target="_blank">Obsługa klawiatury i myszki</a> są oczywiście ważne, ale już na nikim nie robią szczególnego wrażenia. <i>OpenCV</i> jak wiadomo jest przeznaczony do śledzenia i analizy ruchu i to jest jego główna siła.<br />
<br />
W dzisiejszym wpisie zostanie pokazane jak dodać kolejną warstwę interakcji poprzez analizę i detekcję ruchu. Wpis ten będzie naturalnym rozszerzeniem przykładów omawianych wcześniej, dlatego zachęcam do ich wcześniejszej analizy. Szczególnie istotne są dwa z nich:<br />
<ul style="text-align: left;">
<li><a href="http://rpetryniak.blogspot.com/2011/05/opencv-obsuga-klawiatury-i-myszki.html" target="_blank">OpenCV - obsługa klawiatury i myszki</a> [<i>16 maj 2011</i>]</li>
<li><a href="http://rpetryniak.blogspot.com/2011/04/detekcja-ruchu-w-opencv-porownanie.html" target="_blank">Detekcja ruchu w OpenCV - porównanie klatek</a> [<i>6 kwi 2011</i>]</li>
</ul>
Dobra wiadomość jest taka, że dzięki wprowadzeniu <a href="http://rpetryniak.blogspot.com/2011/04/poprawiony-szkielet-aplikacji.html" target="_blank">obiektowego szkieletu aplikacji</a>, rozszerzenie aplikacji o interakcję ruchową będzie niezwykle proste i nie będzie wymagało wielu zmian w kodzie. Dla przykładu kod <i>kulki</i> praktycznie pozostaje bez zmian. Logika aplikacji jest również taka sama jak dla <a href="http://rpetryniak.blogspot.com/2011/05/opencv-obsuga-klawiatury-i-myszki.html" target="_blank">poprzedniego przykładu</a>.<br />
<br />
Pierwszą rzeczą, którą należy zrobić jest dodanie kilku pomocniczych zmiennych, które będą zawierać pośrednie etapu przetwarzania obrazu.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">// plik: <b>interactive_app_tutorial-MainApp.h</b>
class MainApp
{
private:
Mat cam_frame, img_gray, img_first, img_prev, img_diff, img_bin;</pre>
<br />
Następnie należy rozszerzyć funkcję <span style="font-family: 'Courier New', Courier, monospace;"><u>update</u></span> klasy <span style="font-family: 'Courier New', Courier, monospace;"><u>MainApp</u></span> o sprawdzanie na ile każda z kulek pokrywa się z fragmentami obrazu binarnego, gdzie został wykryty ruch.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">// plik: <b>interactive_app_tutorial-MainApp.cpp</b>
for (int k = 0; k < myBall.size(); k++) {
movementAmount = 0;
ball_dim = myBall[k].getDim();
ball_x = myBall[k].getX();
ball_y = myBall[k].getY();
for (int i=(-1*ball_dim); i<ball_dim; i++)
for (int j=(-1*ball_dim); j<ball_dim; j++)
if ( dist(0,0,i,j) < ball_dim ) {
canvas_ind_x = ball_x + i;
canvas_ind_y = ball_y + j;
if ( canvas_ind_x<=0 || canvas_ind_y<=0 ||
canvas_ind_x>CANVAS_WIDTH || canvas_ind_y>CANVAS_HEIGHT)
continue;
if ( img_bin.at<uchar>(canvas_ind_y,canvas_ind_x) > 30) {
movementAmount++;
}
}
if (movementAmount > max_motion_points)
myBall[k].decrementLives();
else
if (!pauseBallsMove) myBall[k].update();
}</pre>
<br />
W powyższym przykładzie widać, że jeśli więcej niż 30 pikseli leży na masce (zmienna <span style="font-family: 'Courier New', Courier, monospace;">img_bin</span>) wskazującej miejsce detekcji ruchu to możemy uznać, że kulka została "dotknięta". Oczywiście można zmienić tą wartość na inną, lub nawet ustalać ją w zależności od wielkości elementu zbijanego. To prawdopodobnie byłoby lepsze rozwiązanie od obecnego, dlatego zachęcam czytelników do samodzielnych eksperymentów.</div>
Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com5tag:blogger.com,1999:blog-19722740.post-6630119501156812302011-08-08T16:12:00.001+02:002011-08-08T19:49:31.969+02:00Zbiór przykładowych aplikacji Qt Quick<div dir="ltr" style="text-align: left;" trbidi="on">Jak widać, patrząc na moje ostatnie wpisy na blogu - a raczej ich brak :-), zrobiłem sobie dłuższą przerwę od <a href="http://rpetryniak.blogspot.com/search/label/meego"><i>MeeGo</i></a> i <a href="http://rpetryniak.blogspot.com/search/label/Qt"><i>Qt</i></a>. Nie wynika to bynajmniej z utraty zainteresowania tymi dwoma technologiami, ale raczej z chronicznego braku czasu. Po rozpoczętej na początku tego roku <a href="http://rpetryniak.blogspot.com/search/label/mobile">serii wpisów dotyczących systemów mobilnych</a>, nadszedł czas na zapoznanie się z mocno ostatnio promowanym <a href="http://qt.nokia.com/qtquick/"><b>Qt Quick</b></a>. Nie będę jednak zbyt wiele pisał o tej technologii, bo przyznam szczerze, że nie wiele się na niej znam. Po przerobieniu jednego prostego kursu, nie będę udawał, że jestem ekspertem w tej dziedzinie :-).<br />
<br />
Pierwsze przymiarki do <i>Qt Quick</i> i <i>QMLa</i> rozpocząłem od przeszukania Internetu za gotowymi przykładami. Najwięcej jest ich oczywiście na stronie <i>Nokii</i> i tam polecam zajrzeć. Niestety jest mały bałagan w ich lokalizacji i mimo prób ich gromadzenia na <a href="http://developer.qt.nokia.com/wiki/Category:Developing_with_Qt::Qt_Quick">Wiki Qt</a> lub też w <a href="http://www.developer.nokia.com/Develop/Qt/Code_examples/Qt_Quick.xhtml">Portalu dla Developerów Noki</a> ciągle znajdują się one w kilku miejscach. Udało mi się nad tym zapanować tworząc odpowiednie kategorie w moich <i>Ulubionych</i>. Oczywiście udostępniam je również Wam.<br />
<br />
<span style="font-size: large;">Przykłady dołączone do oficjalnej dokumentacji:</span><br />
<div><ul><li><a href="http://doc.qt.nokia.com/latest/demos-declarative-calculator.html">Calculator</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-flickr.html">Flickr Mobile</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-minehunt.html">Minehunt</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-photoviewer.html">Photo Viewer</a><a href="http://doc.qt.nokia.com/latest/demos-declarative-photoviewer.html"></a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-rssnews.html">RSS News</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-samegame.html">Same Game</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-snake.html">Snake</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-twitter.html">Twitter Mobile</a></li>
<li><a href="http://doc.qt.nokia.com/latest/demos-declarative-webbrowser.html">QML Web Browser</a></li>
<li><i>Więcej przykładów na <a href="http://doc.qt.nokia.com/latest/qdeclarativeexamples.html">stronie dokumentacji</a>.</i></li>
</ul></div><span style="font-size: large;">Przykłady opisane na Wiki Qt<span style="font-size: small;"> </span></span>(<a href="http://developer.qt.nokia.com/wiki/Category:Developing_with_Qt::Qt_Quick">link</a>)<span style="font-size: large;">:</span><br />
<ul><li><a href="http://developer.qt.nokia.com/wiki/Coffee_Tweed_Demo">Coffee Tweed Demo</a><a href="http://developer.qt.nokia.com/wiki/Coffee_Tweed_Demo"></a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Weather_Demo">Weather Demo</a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Radio_Tuner_Demo">Radio Tuner Demo</a><a href="http://developer.qt.nokia.com/wiki/Radio_Tuner_Demo"></a></li>
<li><a href="http://developer.qt.nokia.com/wiki/In-vehicle_Infotainment_Demo">In-vehicle Infotainment Demo</a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Home_Control_Demo">Home Control Demo</a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Dictomania_Demo">Dictomania Demo</a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Flying_Bus_Game">Flying Bus Game</a><a href="http://developer.qt.nokia.com/wiki/Flying_Bus_Game"></a></li>
<li><a href="http://developer.qt.nokia.com/wiki/How_To_Mingle_QML_Tetrominos_And_Ninjas_Zij_Lost_Tetris_Game">Tetris Game</a></li>
</ul><span style="font-size: large;">Przykłady z portalu dla developerów Noki:</span><br />
<br />
<b>Oficjalnie wspierane</b> (<a href="http://www.developer.nokia.com/Develop/Qt/Code_examples/Qt_Quick.xhtml">link</a>):<br />
<div><ul><li><a href="https://projects.developer.nokia.com/lucid">Lucid Screensaver</a></li>
<li><a href="https://projects.developer.nokia.com/betalabsclient">Beta Labs Client</a></li>
<li><a href="https://projects.developer.nokia.com/mediabrowser">QML Media Browser</a></li>
<li><a href="https://projects.developer.nokia.com/mototrialracer">Moto Trial Racer</a></li>
<li><a href="https://projects.developer.nokia.com/airswype">AirSwype</a></li>
<li><a href="https://projects.developer.nokia.com/qtquickplayground">Qt Quick Playground</a></li>
<li><a href="https://projects.developer.nokia.com/QMLRSSReader">QML RSS Reader</a><a href="https://projects.developer.nokia.com/QMLRSSReader"></a></li>
<li><a href="https://projects.developer.nokia.com/qmlsudokumaster">Sudokumaster Qt Quick</a></li>
<li><a href="https://projects.developer.nokia.com/guitartuner">Guitar Tuner</a><a href="https://projects.developer.nokia.com/guitartuner"></a></li>
<li><a href="https://projects.developer.nokia.com/quickhit">QuickHit</a><a href="https://projects.developer.nokia.com/quickhit"></a></li>
<li><a href="https://projects.developer.nokia.com/qtbubblelevel">Qt Bubble Level</a></li>
<li><a href="https://projects.developer.nokia.com/turntable">DJ Turntable</a></li>
<li><a href="https://projects.developer.nokia.com/compass">Compass</a></li>
<li><a href="https://projects.developer.nokia.com/wwdaemon">WhoWhere Daemon</a></li>
<li><a href="https://projects.developer.nokia.com/QMLRestaurantApp">QML RestaurantApp</a></li>
<li><a href="https://projects.developer.nokia.com/qmultiwinexample">QMultiWinExample</a></li>
</ul><div><b>Rozwijane przez społeczność</b> (<a href="http://www.developer.nokia.com/Develop/Qt/Code_examples/Qt_Quick.xhtml">link</a>) - wybrałem te, które wizualnie zrobiły na mnie największe wrażenie:</div><div><ul><li><a href="https://projects.developer.nokia.com/icthub">ICT Academy Hub</a></li>
<ul><li>Egg Hunter</li>
<li>Submariner</li>
<li>Submariner</li>
</ul><li><a href="https://projects.developer.nokia.com/gNewsReader">gNewsReader</a><a href="https://projects.developer.nokia.com/gNewsReader"></a></li>
<li><a href="https://projects.developer.nokia.com/qtouchandlearn">Touch'n'learn</a></li>
<li><a href="https://projects.developer.nokia.com/SystemInfo">SystemInfo</a></li>
<li><a href="https://projects.developer.nokia.com/m3uplayer">m3uplayer</a></li>
<li><a href="https://projects.developer.nokia.com/nfcchat">Nfc Chat</a></li>
<li><a href="https://projects.developer.nokia.com/battleships">Battleships</a></li>
<li><a href="https://projects.developer.nokia.com/EvidenceHunt">EvidenceHunt Game</a></li>
<li><a href="https://projects.developer.nokia.com/demine">Demine</a></li>
<li><a href="https://projects.developer.nokia.com/butaca">Butaca</a></li>
<li><a href="https://projects.developer.nokia.com/MeasureMee">MeasureMee</a></li>
<li><a href="https://projects.developer.nokia.com/weeklyplanner">QML Weekly Planner</a><a href="https://projects.developer.nokia.com/weeklyplanner"></a></li>
<li><a href="https://projects.developer.nokia.com/meetodo">MeeToDo</a></li>
<li><a href="https://projects.developer.nokia.com/kasvopus">Kasvopus</a></li>
<li><a href="https://projects.developer.nokia.com/qmlbounce">QML Bounce</a></li>
<li><a href="https://projects.developer.nokia.com/qmlbox2ddemo">QML Box2D demo</a></li>
<li><a href="https://projects.developer.nokia.com/colibri">Qt Quick Colibri</a></li>
<li><a href="https://projects.developer.nokia.com/ShoppingList">ShoppingList</a></li>
<li><a href="https://projects.developer.nokia.com/mirrorhouse">MirrorHouse</a></li>
<li><a href="https://projects.developer.nokia.com/eb_media_gallery">EB Media Gallery Demo</a></li>
<li><a href="https://projects.developer.nokia.com/QtQuickTwitterExample">Qt Quick Twitter Example</a></li>
<li><a href="https://projects.developer.nokia.com/WaterBubble">Water Bubble</a></li>
<li><a href="https://projects.developer.nokia.com/qwhatser">Whatser</a></li>
<li><a href="https://projects.developer.nokia.com/hitthemonkey2">Hit the Monkey 2</a></li>
<li><a href="https://projects.developer.nokia.com/QtQuickColouringBook">Qt Quick Colouring Book</a></li>
<li><a href="https://projects.developer.nokia.com/doodledrive">DoodleDrive</a></li>
<li><a href="https://projects.developer.nokia.com/naijanimi">Naijanimi SMS</a></li>
<li><a href="https://projects.developer.nokia.com/wordmaster">WordMaster</a></li>
<li><a href="https://projects.developer.nokia.com/flake_weather">Flake Weather</a></li>
<li><a href="https://projects.developer.nokia.com/PAGADI">Nu, pagadi!</a></li>
<li><a href="https://projects.developer.nokia.com/qmlbites">QML Bites</a></li>
</ul><span style="font-size: large;">Wiki dla społeczności Noki</span> (<a href="http://www.developer.nokia.com/Community/Wiki/Category:Qt_Quick">link</a>)<span style="font-size: large;">:</span></div><div><div><ul><li><a href="http://www.developer.nokia.com/Community/Wiki/A_QML_Memory_Game_Tutorial">A QML Memory Game Tutorial</a><a href="http://www.developer.nokia.com/Community/Wiki/A_QML_Memory_Game_Tutorial"></a></li>
<li><a href="http://www.developer.nokia.com/Community/Wiki/Bubble_Kid_QML_game">Bubble Kid QML game</a></li>
<li><a href="http://www.developer.nokia.com/Community/Wiki/Building_a_CoverFlow_component_with_QML">Building a CoverFlow component with QML</a></li>
<li><a href="http://www.developer.nokia.com/Community/Wiki/Implementing_a_simple_View_Manager_with_QML">Implementing a simple View Manager with QML</a></li>
<li><a href="http://www.developer.nokia.com/Community/Wiki/Custom_QML_Component:_Website_Thumbnails">Custom QML Component: Website Thumbnails</a><a href="http://www.google.com/url?q=http%3A%2F%2Fwww.developer.nokia.com%2FCommunity%2FWiki%2FCustom_QML_Component%3A_Website_Thumbnails&sa=D&sntz=1&usg=AFrqEzcVVoCuQiuG3aQV9i4k9-wVbHYLuw" style="color: #0033cc; text-decoration: underline;"></a></li>
<li><a href="http://www.developer.nokia.com/Community/Wiki/Can_QtQuick_create_a_Windows_Phone_Hub">Can QtQuick create a Windows Phone Hub</a></li>
</ul></div></div><span style="font-size: large;">Inne przykłady znalezione na stronie Qt:</span></div><div><ul><li><a href="http://developer.qt.nokia.com/wiki/Towers_lasers_and_spacecrafts_example">Towers lasers</a></li>
<li><a href="http://www.developer.nokia.com/Community/Wiki/QmlWebMapsLocation">QmlWebMapsLocation</a></li>
</ul><b>Kursy i poradniki pokazujące jak coś zrobić:</b></div><ul><li><a href="http://developer.qt.nokia.com/wiki/Qt_Quick_Carousel">Obracające się menu</a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Qt_Quick_Image_Viewer">Płynne przewijanie obrazów</a><a href="http://developer.qt.nokia.com/wiki/Qt_Quick_Image_Viewer"></a></li>
<li><a href="http://developer.qt.nokia.com/wiki/Drag_and_Drop_within_a_GridView">Przeciąganie i opuszczanie elementów na siatce:</a></li>
</ul><b>Repozytoria:</b><br />
<ul style="text-align: left;"><li><a href="https://www.gitorious.org/qt-training">https://www.gitorious.org/qt-training</a></li>
<li><a href="http://gitorious.org/ofi-labs/x2">http://gitorious.org/ofi-labs/x2</a></li>
<li><a href="http://qt.gitorious.org/qt-labs/mobile-demos">http://qt.gitorious.org/qt-labs/mobile-demos</a></li>
<li><a href="http://qt.gitorious.org/qt-labs/graphics-dojo">http://qt.gitorious.org/qt-labs/graphics-dojo</a></li>
<li><a href="https://gitorious.org/search?q=qml">https://gitorious.org/search?q=qml</a></li>
<li><a href="https://gitorious.org/search?q=category%3Aqml">https://gitorious.org/search?q=category%3Aqml</a></li>
</ul><b>Przykłady i projekty społeczności:</b><br />
<ul style="text-align: left;"><li><a href="http://openbossa.indt.org/canola2/index.html">Canola Media Center</a></li>
</ul>Patrząc na tą listę nie wiem czy udało mi się znaleźć dużo, czy mało przykładów aplikacji zaimplementowanych z użyciem <i>Qt Quick. </i>Dla osób raczkujących w tej technologi, tak jak ja, jest ich jednak w zupełności wystarczająco na start.<br />
<br />
<ul style="text-align: left;"></ul></div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-84217205310264149382011-07-25T23:32:00.000+02:002011-07-25T23:32:20.051+02:00"Generative art" teraz w moim plecaku<div dir="ltr" style="text-align: left;" trbidi="on">Nareszcie przyszła druga zamówione przez mnie książka w <i>Amazon</i>. Czekałem na nią długo - ok. 2 miesiące. Na szczęście w tym czasie spokojnie mogłem przejrzeć "<a href="http://rpetryniak.blogspot.com/2011/06/formcode-w-mojej-biblioteczce.html">Form+Code</a>". <br />
<br />
Pierwsze moje wrażenie po otwarciu "<i><b>Generative art</b></i>" było takie: "to nie tak miało być! spodziewałem się czegoś innego!". Moje lekkie rozczarowanie wynikało po prostu stąd, że po obejrzeniu przykładowego rozdziału, który jest dostępny na <a href="http://zenbullets.com/blog/?page_id=799">stronie autora książki</a> <i><b>Matta Pearsona</b></i> (podaje link, aby było szybciej: <a href="http://www.manning.com/pearson/GenArt-Sample-Chapter-1.pdf">Rozdział 1</a>) spodziewałem się w pełni ilustrowanej książki, coś w rodzaju katalogu projektów i opisu dobrych praktyk.<br />
<br />
Na szczęście, to było tylko pierwsze i jak się okazało powierzchowne wrażenie. No bo jak budować opinie na temat książki przeglądając jedynie parę stron. Wystarczy poświecić jej trochę więcej czasu, aby dostrzec, że jest ona pełna opisów dobrych praktyk, zawiera naprawdę sporo wartościowych przykładów i pokazuje krok po kroku coraz bardziej zaawansowane techniki generowania ilustracji komputerowych. <br />
<br />
Więcej nie zdradzam. Zachęcam do przejrzenia :-)<br />
<br />
I jeszcze jedna dobra wiadomość: po zakupie książki <i>ebooka</i> dostajemy gratis :-D.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmO5qFEe8cB9USmesY9qOIXvGsXR4q4Xpa2Hs2tl-cLsmnnkYIvLfqTGP-PZ9vkgDoh2IZTheTkya7ddNpLvD49AuTo0LVJWLz5CInoFn2psTE7uaRXWLGHe5f91CcrtNU9us9/s1600/book-generative_art-pearson.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmO5qFEe8cB9USmesY9qOIXvGsXR4q4Xpa2Hs2tl-cLsmnnkYIvLfqTGP-PZ9vkgDoh2IZTheTkya7ddNpLvD49AuTo0LVJWLz5CInoFn2psTE7uaRXWLGHe5f91CcrtNU9us9/s400/book-generative_art-pearson.png" width="400" /></a></div><br />
<br />
<br />
<br />
</div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com2tag:blogger.com,1999:blog-19722740.post-12143564435831617992011-07-12T23:58:00.001+02:002011-07-13T11:09:52.803+02:00Google MatrixWszystko wskazuje na to, że <i>Google</i> wie o mnie wszystko. Nie tylko to z kim koresponduję, to o czym wtedy piszę, to czego szukam w Internecie i komu robię fotki wakacjach.<br />
<br />
Wie również z jakiego systemu korzystam i (o zgrozo) z jakiej przeglądarki nie korzystam - a wg Google powinienem ;-)<br />
<br />
Najlepszym przykładem tego o czym właśnie piszę, może być reklama jaką dzisiaj zaserwował mi <i>Google</i> w swoim <i>czytniku RSSów</i>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDH81p6Sly3aEk7acyFj7TC2Bw_M5R_DoeDikKwZwSlVXu1CcUN2AMrXqtQnuF60chHoQFvIVfSY6BGxOGhOAq_cErWhBoOr8TRVHYuvXJ24pzBS4UXs5iIkeZZClwnMRaMAY6/s1600/google_matrix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDH81p6Sly3aEk7acyFj7TC2Bw_M5R_DoeDikKwZwSlVXu1CcUN2AMrXqtQnuF60chHoQFvIVfSY6BGxOGhOAq_cErWhBoOr8TRVHYuvXJ24pzBS4UXs5iIkeZZClwnMRaMAY6/s1600/google_matrix.png" /></a></div><br />
<br />
Czyżby nadchodził Google Matrix?Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-77706735454520817862011-06-13T09:38:00.000+02:002011-07-25T23:32:54.006+02:00"Form+Code" w mojej biblioteczce<div dir="ltr" style="text-align: left;" trbidi="on">Po kilku tygodniach czekania w końcu doszła do mnie nowa książka. "<i><b>Form+Code</b></i>" zamówiłem jakiś czas temu przez <i>Amazon</i> razem z innym tytułem, o którym napiszę jaki mi go prześlą (ma być pod koniec czerwca, bo na razie nie mają go na stanie). Książka, która do mnie dotarła kilka dni temu, tak jak przewidywałem, nie jest za bardzo techniczno - informatyczna. Zawiera ona za to wiele opisów wdrożeń i instalacji światowej czołówki artystów zajmujących się sztuką generowaną (<i>Generative Art</i>) i kreatywnym kodowaniem (<i>Creative Coding</i>). Na szczęści książka zawiera również algorytmiczny opis kilkunastu ciekawych efektów, dzięki czemu można trochę podejrzeć jakich metod i narzędzi używają najlepsi w branży :-)<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtbTo7ojmnFqJMUYrBgvhVa4dvjPzPpabCn0jUpig6SksBcUd7SjkhkUrJhHOZC8Q2ysnyegkMVkDqhGTWquK_mM9HjejGFsXGDhcs7QmELk4wVQ5xRze3SnAN_k60ABAOxGSE/s1600/form_plus_code.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtbTo7ojmnFqJMUYrBgvhVa4dvjPzPpabCn0jUpig6SksBcUd7SjkhkUrJhHOZC8Q2ysnyegkMVkDqhGTWquK_mM9HjejGFsXGDhcs7QmELk4wVQ5xRze3SnAN_k60ABAOxGSE/s400/form_plus_code.jpg" width="400" /></a></div><br />
Bardzo cennym uzupełnieniem książki jest również obszerna <b><a href="http://formandcode.com/">strona internetowa</a></b> jej poświęcona. Można na niej zobaczyć niektóre strony z książki - jest ona głównie ilustrowana więc jest co oglądać, przejrzeć spis treści, a co najważniejsze pobrać przykładowe programy dołączone do książki.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://formandcode.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="32" src="http://formandcode.com/_media/title.png" width="400" /></a></div><br />
Polecam!</div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-31978485062062927462011-05-16T22:59:00.002+02:002011-05-16T23:24:51.221+02:00OpenCV - obsługa klawiatury i myszki<div dir="ltr" style="text-align: left;" trbidi="on"><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_4" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/logo/github-logo1.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_4">Pełny kod źródłowy</a></td></tr>
</tbody></table>Kontynuując rozpoczęty ponad miesiąc temu cykl wpisów dotyczących pisania aplikacji interaktywnych nadszedł czas na wprowadzenie kolejnego elementu do wcześniej <a href="http://rpetryniak.blogspot.com/2011/04/poprawiony-szkielet-aplikacji.html">zaproponowanego szablonu</a>. Dzisiaj zostanie omówiona zasadnicza kwestia pozwalająca na wprowadzenie przynajmniej podstawowej interakcji do programu: <u>obsługa klawiatury i myszy</u>. Oczywiście dla każdego programisty naturalnym jest to, że program z definicji coś takiego posiada, tzn. że możemy klikać na przyciski, przesuwać suwaki, wybierać zakładki i wpisywać tekst do formularzy. Jednak w przypadku <i>OpenCV</i>, należy pamiętać, że jest to biblioteka głównie do przetwarzania obrazu i chociaż obsługa klawiatury i myszy jest dostępna, to nie jest ona widoczna na pierwszy rzut oka.<br />
<br />
W tym wpisie poza omówieniem mechanizmów <i>API</i> do obsługi klawiatury i myszy, zostanie dodatkowo rozszerzony omawiany we wcześniejszych wpisach przykład z kulkami. Zachęcam również do samodzielnych testów i modyfikacji udostępnionego kodu.<br />
<br />
<span style="font-size: large;">Obsługa zdarzeń myszy</span><br />
<br />
Myszka w <i>OpenCV</i> jest w pełni obsługiwana za pomocą modułu <b><span style="font-family: "Courier New",Courier,monospace;">highgui</span></b>, który jest również odpowiedzialny za wyświetlanie obrazków. Obsługę taką możemy włączyć za pomocą funkcji <span style="font-family: "Courier New",Courier,monospace;">cvSetMouseCallback</span>, w której przywiązujemy do danego okienka funkcję obsługującą zdarzenia myszy. Dodatkowo, ponieważ jest to funkcja globalna, a nie lokalna naszej klasy <span style="font-family: "Courier New",Courier,monospace;">MainApp</span>, należy do niej przekazać wskaźnik do aktualnego obiektu. Dzięki temu będziemy mieć dostęp do jego funkcji składowych. Podsumowując: należy dodać do funkcji <i>MainApp::run()</i><b> </b>następującą linijkę:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">cvSetMouseCallback(win_canvas, mouse_callback, this);</pre><br />
Drugi parametr tej funkcji jest wskaźnikiem do właściwej funkcji obsługującej myszkę. Funkcja ta odbiera nie tylko zdarzenie jakie wystąpiło (naciśnięcie i zwolnienie klawisza, podwójny klik, przesunięcie myszki), ale również bieżące współrzędne wskaźnika myszki. <br />
<br />
<pre class="prettyprint lang-cpp notranslate">void mouse_callback(int event, int x, int y, int flags, void* param)
{
MainApp *app = ((MainApp *)param);
switch (event) {
case CV_EVENT_LBUTTONDBLCLK:
app->mouseDoubleClick(x,y,APP_MOUSE_LEFT); break;
case CV_EVENT_RBUTTONDBLCLK:
app->mouseDoubleClick(x,y,APP_MOUSE_RIGHT); break;
case CV_EVENT_MBUTTONDBLCLK:
app->mouseDoubleClick(x,y,APP_MOUSE_MIDDLE); break;
case CV_EVENT_LBUTTONDOWN:
app->mousePressed(x,y,APP_MOUSE_LEFT); break;
case CV_EVENT_RBUTTONDOWN:
app->mousePressed(x,y,APP_MOUSE_RIGHT); break;
case CV_EVENT_MBUTTONDOWN:
app->mousePressed(x,y,APP_MOUSE_MIDDLE); break;
case CV_EVENT_LBUTTONUP:
app->mouseReleased(x,y,APP_MOUSE_LEFT); break;
case CV_EVENT_RBUTTONUP:
app->mouseReleased(x,y,APP_MOUSE_RIGHT); break;
case CV_EVENT_MBUTTONUP:
app->mouseReleased(x,y,APP_MOUSE_MIDDLE); break;
case CV_EVENT_MOUSEMOVE:
app->mouseMoved(x,y); break;
}
}</pre><br />
Oczywiście w tym miejscu można już zaprogramować co ma się wydarzyć po wystąpieniu konkretnego zdarzenia, ale proponuję przekazać podejmowanie tej decyzji do wnętrza klasy, do odpowiednich funkcji. Oto one:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">void MainApp::mouseMoved(int mouseX, int mouseY ){
if (m_mousePressed) mouseDragged(mouseX, mouseY);
}
void MainApp::mouseDragged(int mouseX, int mouseY){
}
void MainApp::mousePressed(int mouseX, int mouseY, int button){
m_mousePressed = true;
}
void MainApp::mouseReleased(int mouseX, int mouseY, int button){
m_mousePressed = false;
}
void MainApp::mouseDoubleClick(int mouseX, int mouseY, int button){
}</pre><br />
Jak widać nie ma tutaj nic szczególnego - kilka pustych funkcji. Czekają one po prostu na oprogramowanie przez użytkownika, a ich treść zależy od tego co w danym momencie chcemy osiągnąć. Na końcu wpisu zostanie podany przykład jak za pomocą kliknięcia myszką w element usunąć go z ekranu, więc polecam doczytać do końca :-).<br />
<br />
<span style="font-size: large;">Obsługa zdarzeń klawiatury</span><br />
<br />
Z klawiaturą mamy już dużo łatwiej, aniżeli z myszką. Można zupełnie obejść się bez dodatkowych funkcji i wszystko zaprogramować w ciele funkcji, która zawiera pętlę główną programu. Wystarczy w tym celu skorzystać z funkcji <i>cvWaitKey(int)</i>, aby sprawdzić czy użytkownik wcisnął jakiś klawisz. <br />
<br />
<pre class="prettyprint lang-cpp notranslate">int key;
while(1) {
key = cvWaitKey(DELAY); // wait for keyboard input
if (key == 'q') break; // 'q' pressed, quit the program
if (key != -1 ) keyPressed(key);
update();
draw();
imshow(win_canvas, canvas);
}</pre><br />
Podejście to pomimo tego, że poprawne, może niepotrzebnie wprowadzić nam zamieszanie nadmiarem kodu w ważnym miejscu programu. Polecam zatem wprowadzić dodatkową funkcję, która będzie zajmowała się wyłącznie reagowaniem na zdarzenia klawiatury. Jej deklaracja jest następująca:<br />
<br />
<b></b><br />
<pre class="prettyprint lang-cpp notranslate">void MainApp::keyPressed(int key){
switch (key){
case ' ':
pauseBallsMove = !pauseBallsMove;
break;
}
}</pre><br />
Patrząc na tą funkcję widać, że została zdefiniowana akcja, która wydarzy się po wciśnięciu przez użytkownika <i>spacji</i>. Nie dzieje się tutaj nic więcej, aniżeli prosta negacja zmiennej logicznej <i>pauseBallsMove</i>. Zmienna ta może się przydać np. do zatrzymania ruchu elementów w naszym przykładowym programie:<br />
<br />
<b>MainApp::update() </b><br />
<pre class="prettyprint lang-cpp notranslate">if (!pauseBallsMove) {
for (int i = 0; i < myBall.size(); i++) {
myBall[i].update();
}
}</pre><br />
<span style="font-size: large;">Przykładowa aplikacja</span><br />
<br />
A teraz obiecany przykład :-) Nie zawiera on szczególnie wiele kodu, ale efekt jaki pozwala osiągnąć wydaje się całkiem interesujący. Na początek polecam dodać dodatkowe polecenia obsługujące zdarzenie naciśnięcia klawisza <b style="font-family: "Courier New",Courier,monospace;">'a'</b> i <b><span style="font-family: "Courier New",Courier,monospace;">'d'</span></b>. Naciśniecie pierwszej literki będzie powodować dodanie kolejnych kulek na ekranie, natomiast naciśnięcie drugiej zmniejszenie ich liczny.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">case 'a':
myBall.push_back( Ball(&canvas) );
break;
case 'd':
if (myBall.size()>=1) myBall.pop_back();
break;</pre><br />
Kolejną zmianą jaką można wprowadzić jest dodanie kilku linii kodu, reagujących na klikniecie myszką. Uzupełniamy ciało funkcji <i>mousePressed</i> następującym kodem:<br />
<b><br />
</b><br />
<pre class="prettyprint lang-cpp notranslate">for(int i=0; i < myBall.size(); i++) {
if(myBall[i].checkMouseOver(mouseX, mouseY)) {
myBall[i].decrementLives();
if(myBall[i].isEndLive()) myBall.erase(myBall.begin()+i);
}
}</pre><br />
Teraz jeszcze wystarczy dodać kilka nowych funkcji do klasy <i>Ball</i>:<br />
<b><br />
</b><br />
<pre class="prettyprint lang-cpp notranslate">bool Ball::checkMouseOver(int _mouseX, int _mouseY) {
bool mouseIsOver = false;
if ( dist(_mouseX,_mouseY,x,y) < dim ) mouseIsOver = true;
return mouseIsOver;
}
void Ball::decrementLives() {
lives--;
}
bool Ball::isEndLive() {
return (lives <= 0);
}
</pre><br />
i chyba nie trudno się domyślić jaki w ten sposób efekt możemy uzyskać :-)<br />
<br />
Dla osób zniecierpliwionych lub takich, które nie mają czasu na samodzielną kompilację przykładu, przygotowałem krótki filmik pokazujący efekty wprowadzenia obsługi klawiatury i myszki do omawianej aplikacji:<br />
<br />
<div style="text-align: center;"><iframe allowfullscreen="" frameborder="0" height="314" src="http://www.youtube.com/embed/Ui4h_AGoohc" width="500"></iframe><br />
<a href="http://www.youtube.com/watch_popup?v=Ui4h_AGoohc&vq=hd720" target="_blank"><i>Oglądaj film w rozdzielczości HD na pełnym ekranie.</i></a></div><br />
</div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-16465136184632778972011-04-19T09:19:00.000+02:002011-05-16T23:24:51.221+02:00Repozytorium dla przykładów OpenCV na GitHubW ostatnim czasie na blogu <a href="http://rpetryniak.blogspot.com/search/label/OpenCV">pojawiło się kilka wpisów</a> o <i>OpenCV</i>. Wiele z nich zawierało przykładowy kod źródłowy, który wklejałem we fragmentach, aby nie wydłużać samych postów. Były to głównie kluczowe fragmenty opisywanych programów i nie zawierały one najczęściej początku i końca pliku. Wiem, że nie jest to żaden kłopot dla osób, które miały do czynienia z <i>OpenCV</i>, ale dla początkującego użytkownika może to być mały problem. Łatwo przecież zrobić drobny błąd, literówkę lub coś przeoczyć, a trudniej to później znaleźć. W związku z tym utworzyłem publiczne repozytorium kodu źródłowego <i>GitHub</i>, do którego wypchnąłem (<i>push</i> - w terminologii <i>Git</i>) opracowane do tej pory przykłady. Do repozytorium można przejść klikając w poniższy obrazek.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/_AeFiJN9phoM/Ta0zxds0HGI/AAAAAAAAGBw/sH1ntbh9WT8/opencv-nowe_repozytorium_na_github.png" /></a></div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-31906048559703475272011-04-11T23:18:00.003+02:002011-05-16T23:24:51.222+02:00Poprawiony szkielet aplikacji interaktywnej dla OpenCV<div dir="ltr" style="text-align: left;" trbidi="on"><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_3" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/logo/github-logo1.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_3">Pełny kod źródłowy</a></td></tr>
</tbody></table>Kilka dni temu zaproponowałem na tym blogu <a href="http://rpetryniak.blogspot.com/2011/04/obiektowy-szkielet-aplikacji.html">obiektowy szkielet aplikacji interaktywnej dla OpenCV</a>. W dzisiejszym wpisie chciałbym pokazać jak można zrobić to jeszcze lepiej, aby kod był nie tylko bardziej obiektowy, ale również miał lepiej zorganizowaną strukturę. Pomimo tego, że przedstawiony tam schemat aplikacji całkiem nieźle się sprawdza, a kod jest w miarę czytelny, ma on kilka wad i usterek, które zapewne niejeden zaawansowany programista C++ szybko mógłby wytknąć. Oto dwie z nich:<br />
<ul><li>Cały kod programu został umieszczony w jednym pliku. Dla małych projektów jest to w miarę dobre rozwiązanie, jednak przy większych aplikacjach może być uciążliwe. Umieszczanie całego kodu w jednym pliku łamie również powszechnie stosowaną zasadę umieszczania każdej klasy w osobnym pliku. Inną powszechnie znaną praktyką, która nie została tam zastosowana, jest rozdział deklaracji klasy od jej ciała i umieszczenie ich osobno w pliku nagłówkowym (*.h) i w pliku z implementacją funkcji (*.cpp).</li>
<li>Trudno nazwać przedstawiony w poprzednim wpisie szkielet za w pełni obiektowy, jeśli znajduje się tam jedna klasa, która zawiera całą funkcjonalność aplikacji. Szczególnie problem jest widoczny w braku osobnej reprezentacji obiektowej dla znajdujących się tam kulek. Problem ten nie jest szczególnie zauważalny w przypadku istnienia jednej kulki, ale jest już odczuwalny w przypadku ich większej ilości. Wystarczy spojrzeć na funkcję <i>MainApp::update()</i> i spróbować sobie wyobrazić, jak mogłaby ona wyglądać, jeśli trzeba by było zarządzać ruchem i rysowaniem np. 10 kulek. Widać wyraźnie, że coś jest tutaj nie tak i wręcz konieczne wydaje się rozszerzenie modelu obiektowego własnie o te obiekty, które są rysowane na ekranie, a które mają w miarę niezależny schemat poruszania się. </li>
</ul>Patrząc na te problemy postanowiłem szybko wprowadzić niezbędne poprawki, aby wyeliminować opisane niedogodności. Z jednego pliku powstało ich sześć. Dodatkowo utworzyłem klasę <span class="Apple-style-span" style="font-family: 'Courier New',Courier,monospace;"><b>Ball</b></span> reprezentująca ruch i wygląd kulek. Pozwoliło mi to na wprowadzenie łatwej personalizacji dla każdej kulki z osobna. Struktura obydwu klas (<i>MainApp</i> i <i>Ball</i>) została rozdzielona na pliki nagłówkowe i pliki z implementacją ich funkcji składowych.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Krótka charakterystyka szkieletu aplikacji interaktywnej OpenCV</span><br />
<br />
<b>interactive_app_tutorial-main.cpp</b> - plik ten zawiera jedynie funkcję główną programu <i>main()</i>, której jedynym zadaniem jest uruchomienie i wyświetlenie głównego okna aplikacji reprezentowanego przez klasę <i>MainApp</i>. Dodatkowo na początku tego pliku inicjalizowany jest generator liczb losowych <i>OpenCV</i> wartością równą liczbie milisekund, która upłynęła od ustalonej daty w przeszłości (w systemie <i>Linux</i> jest to 1 styczeń 1970 roku).<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "interactive_app_tutorial-MainApp.h"
using namespace cv;
RNG rng(cvGetTickCount());
int main(int, char**)
{
MainApp::getInstance().run();
return 0;
}</pre><br />
<b>interactive_app_tutorial-util.h</b> - tutaj najlepiej umieszczać funkcje ogólnego przeznaczenia oraz funcke narzędziowe, które mogą się przydać w różnych miejscach programu. Na razie znajduje się tu jedynie funkcja zwracająca składowe koloru, dla przekazanej jako parametr liczby.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#ifndef UTIL_H
#define UTIL_H
static Scalar randomColor(RNG& rng)
{
int icolor = (unsigned)rng;
return Scalar(icolor&255, (icolor>>8)&255, (icolor>>16)&255);
}
#endif</pre><br />
<b>interactive_app_tutorial-MainApp.h</b> - jest to plik nagłówkowy zawierający deklarację klasy <i>MainApp</i> i jej składowych. Na pierwszy rzut nie widać, aby coś się zmieniło względem szkieletu aplikacji opisanego w poprzednim wpisie. Jednak jak będzie to widać dalej, implementacja poszczególnych funkcji jest już zupełnie inna. Patrząc na zmienne prywatne jakie tutaj zostały zadeklarowane, można zauważyć, że klasa będzie zarządzać trzema kulkami. Jak sobie poradzić z tym ograniczeniem zostanie pokazane na końcu tego wpisu.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#ifndef MAINAPP_H
#define MAINAPP_H
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "interactive_app_tutorial-Ball.h"
using namespace cv;
//MainApp singelton class
class MainApp
{
private:
Mat canvas; // Image for drawing
Scalar bgr_color; // Background color
Ball *myBall_1, *myBall_2, *myBall_3;
public: // Some global params:
static int DELAY;
static int CANVAS_WIDTH;
static int CANVAS_HEIGHT;
private:
MainApp() {}
MainApp(const MainApp &);
MainApp& operator=(const MainApp&);
void setup(); // Initial commands for setup processing
void update(); // Commands to modify the parameters
void draw(); // Drawing functions:
public:
static MainApp& getInstance()
{
static MainApp instance;
return instance;
}
// Main loop function with displaying image support
// and handle mouse and keyboard events
void run();
};
#endif</pre><br />
<b>interactive_app_tutorial-MainApp.cpp</b> - plik ten zawiera właściwą implementację funkcji zawartych w klasie <i>MainApp</i>. Nie są one jakość szczególnie interesujące ponieważ wywołują jedynie funkcje składowe klasy <i>Ball</i> o identycznych nazwach. <br />
<br />
<pre class="prettyprint lang-cpp notranslate">#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "interactive_app_tutorial-MainApp.h"
using namespace cv;
// Best place to initialize global MainApp params:
int MainApp::DELAY = 5;
int MainApp::CANVAS_WIDTH = 500;
int MainApp::CANVAS_HEIGHT= 350;
void MainApp::run() {
setup();
const char *win_canvas = "Canvas";
namedWindow(win_canvas, CV_WINDOW_AUTOSIZE);
while (cvWaitKey(4) == -1) {
update();
draw();
imshow(win_canvas, canvas);
waitKey(DELAY);
}
}
void MainApp::setup()
{
bgr_color = Scalar(200,200,200);
canvas = Mat(CANVAS_HEIGHT, CANVAS_WIDTH, CV_8UC3, bgr_color);
myBall_1 = new Ball(&canvas);
myBall_2 = new Ball(&canvas);
myBall_3 = new Ball(&canvas);
}
void MainApp::draw() {
canvas = bgr_color;
myBall_1->draw();
myBall_2->draw();
myBall_3->draw();
}
void MainApp::update()
{
myBall_1->update();
myBall_2->update();
myBall_3->update();
}</pre><br />
<b>interactive_app_tutorial-Ball.h</b> - tak naprawdę to tutaj zaczynają się większe zmiany względem <a href="http://rpetryniak.blogspot.com/2011/04/obiektowy-szkielet-aplikacji.html">poprzedniego szkieletu aplikacji</a>. Plik ten zawiera deklarację nowej klasy <i>Ball</i>, która już sama będzie dbać o swój wygląd, wyrysowanie siebie na ekranie oraz wyliczenie właściwej lokalizacji dla kolejnej iteracji. Sprawą, na którą należy tutaj zwrócić szczególną uwagą jest postać konstruktorów. Konstruktor domyślny <i>Ball()</i> został specjalnie zablokowany, aby zachęcić (zmusić ;-)) użytkownika do korzystania z dodatkowego konstruktora, który jako parametr przyjmuje wskaźnik do struktury <i>cv::Mat</i>, po której będzie można rysować. Generalnie powinien być tutaj przekazywany wskaźnik do głównego obrazka z klasy <i>MainApp</i>, na którym odbywa się rysowanie.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#ifndef BALL_H
#define BALL_H
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
//Ball class
class Ball
{
private:
Mat *canvas;
float x, y, dim;
float speedX, speedY;
Scalar color;
Ball();
void setup();
public:
Ball(Mat *can)
{
canvas = can;
setup();
}
void update();
void draw();
};
#endif</pre><br />
<b>interactive_app_tutorial-Ball.cpp</b> - osoby, które już wcześniej analizowały poprzedni szkielet aplikacji zapewne zauważyły, że istotna część treści funkcji <i>MainApp::update()</i> i <i>MainApp::draw()</i> zostały przeniesione do funkcji o identycznych nazwach w klasie <i>Ball</i>. Jest to najważniejsza zmiana w nowym szablonie. Teraz to każda kulka samodzielnie dba o siebie i kontroluje swoje zachowanie.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "interactive_app_tutorial-Ball.h"
#include "interactive_app_tutorial-MainApp.h"
#include "interactive_app_tutorial-util.h"
using namespace cv;
extern RNG rng;
void Ball::setup()
{
x = rng.uniform(0, MainApp::CANVAS_WIDTH); // give some random positioning
y = rng.uniform(0, MainApp::CANVAS_HEIGHT);
speedX = rng.uniform((float)-2, (float)2); // and random speed and direction
speedY = rng.uniform((float)-2, (float)2);
dim = rng.uniform(20,60);
color = randomColor(rng);
}
void Ball::update()
{
if(x < 0 ){
x = 0;
speedX *= -1;
} else if(x > MainApp::CANVAS_WIDTH){
x = MainApp::CANVAS_WIDTH;
speedX *= -1;
}
if(y < 0 ){
y = 0;
speedY *= -1;
} else if(y > MainApp::CANVAS_HEIGHT){
y = MainApp::CANVAS_HEIGHT;
speedY *= -1;
}
x+=speedX;
y+=speedY;
}
void Ball::draw()
{
circle(*canvas, Point(x,y), dim, color,-1,CV_AA);
}</pre><br />
<b>CMakeLists.txt</b> - plik ten zawiera konfigurację procesu kompilacji <i>CMake</i> dla omówionego przykładu. Wcześniej był tylko jeden plik, teraz jest ich sześć. Aby się w tym nie pogubić warto skorzystać właśnie z <i>CMake</i>.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">PROJECT(OpenCvInteractiveAppsAndExamples-InteractiveAppTutorial2)
cmake_minimum_required(VERSION 2.8)
FIND_PACKAGE( OpenCV REQUIRED )
ADD_EXECUTABLE(interactive_app_tutorial_2 interactive_app_tutorial-main.cpp interactive_app_tutorial-MainApp.cpp interactive_app_tutorial-Ball.cpp)
TARGET_LINK_LIBRARIES(interactive_app_tutorial_2 ${OpenCV_LIBS})</pre><br />
<span class="Apple-style-span" style="font-size: large;">Drobne poprawki na koniec</span><br />
<br />
Wydawać by się mogło, ze w opisanym powyżej szkielecie aplikacji <i>OpenCv</i> wszystko jest OK, ale jeden szczegół na dłuższą metę może okazać się bardzo uciążliwy. W tym momencie w klasie <i>MainApp</i> zosłao z góry określone ile kulek będzie rysowane na ekranie. Trzy kulki to trochę mało. Co by było gdyby trzeba ich było narysować np. trzysta? Najlepiej w takiej sytuacji skorzystać z dynamicznie rozszerzanych kontenerów dostępnych w C++. Klasa <i>vector</i> będzie w zupełności wystarczająca. Na początek warto zmienić deklarację klasy <i>MainApp</i> usuwając z niej zmienne lokalne <i>*myBall_1, *myBall_2, *myBall_3</i>, a wprowadzając na to miejsce deklarację wektora <span class="Apple-style-span" style="background-color: #eeeeee; font-family: 'Courier New',Courier,monospace;">vector <Ball> myBall;</span>. Ja dodatkowo wprowadziłem jeszcze jedną zmienną statyczną, która będzie regulować liczbę kulek wyświetlanych na ekranie. Po tych zmianach kod kod może wyglądać jak poniżej:<br />
<br />
<b>interactive_app_tutorial-MainApp.h</b><br />
<br />
<pre class="prettyprint lang-cpp notranslate">private:
vector <Ball> myBall;
//...
public:
static int INITIAL_BALLS_NUMBER;
//...</pre><br />
Kolejna zmiana, która należy wprowadzić to aktualizacja funkcji <i>setup()</i>, <i>update()</i> i <i>draw()</i> klasy <i>MainApp</i>. Teraz one powinny wyglądać następująco:<br />
<br />
<b>interactive_app_tutorial-MainApp.cpp</b><br />
<br />
<pre class="prettyprint lang-cpp notranslate">void MainApp::setup()
{
bgr_color = Scalar(200,200,200);
canvas = Mat(CANVAS_HEIGHT, CANVAS_WIDTH, CV_8UC3, bgr_color);
for (int i = 0; i < INITIAL_BALLS_NUMBER; i++){
myBall.push_back( Ball(&canvas) );
}
}
void MainApp::draw() {
canvas = bgr_color;
for (int i = 0; i < myBall.size(); i++){
myBall[i].draw();
}
}
void MainApp::update()
{
for (int i = 0; i < myBall.size(); i++){
myBall[i].update();
}
}</pre><br />
Ostatecznie aplikacja po powyższych zmianach i ustawieniu liczby kulek na 30 może dać rezultat podobny jak ten na obrazku poniżej: <br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_AeFiJN9phoM/TaNoDygWbDI/AAAAAAAAGBI/pYkugIXp7k0/OpenCv-interactive_app_tutorial_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/_AeFiJN9phoM/TaNoDygWbDI/AAAAAAAAGBI/pYkugIXp7k0/OpenCv-interactive_app_tutorial_2.png" /></a></div><br />
W miarę wolnego czasu w kolejnych wpisach będę chciał pokazać jak wprowadzić do powyższego szablonu możliwości interakcji za pomocą klawiatury i myszki oraz za pomocą ruchu zarejestrowanego kamerą.</div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com1tag:blogger.com,1999:blog-19722740.post-91969388308243734112011-04-07T23:31:00.002+02:002011-05-16T23:27:05.209+02:00Obiektowy szkielet aplikacji interaktywnej dla OpenCV<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_1" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/logo/github-logo1.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/InteractiveAppTutorial/InteractiveAppTutorial_1">Pełny kod źródłowy</a></td></tr>
</tbody></table>Biblioteka <i>OpenCV</i> pomimo tego, że zawiera sporo konstrukcji obiektowych i pozwala pisać programy w C++, sama w sobie nie precyzuje i nie podpowiada jaką strukturę powinien mieć dobrze napisany kod programu. Wpływa to oczywiście na morale wielu programistów, którzy widząc, że można napisać kod liniowo w całości w funkcji <i>main</i>, idą na skróty i własnie tak robią. Nie chciałbym tutaj nikogo szczególnie piętnować ;), ale większość przykładowych programów, które ostatnio analizowałem tak własnie jest napisana. Dotyczy to również standardowych przykładów dołączonych do biblioteki <i>OpenCV</i>. Oczywiście tak nie musi być. Programy napisane w <i>OpenCV</i> przy zastosowaniu podstawowych konstrukcji obiektowych mogą być nie tylko czytelniejsze dla osoby, która je pierwszy raz przegląda, ale również znacznie łatwiejsze w pielęgnacji i dalszym rozwoju dla osoby lub zespołu, który taką aplikację napisał.<br />
<br />
W tym wpisie chciałem się podzielić szablonem aplikacji <i>OpenCV</i>, którego ostatnio używam. Jest to szablon, który może być szczególnie użyteczny dla aplikacji interaktywnych, w których użytkownik ma wpływ na zachowanie wyświetlanych elementów. Przygotowując ten szablon starałem się zachować podejście znane z programowania aplikacji okienkowych, gdzie pracujemy raczej na komponentach odnoszących się do interfejsu użytkownika, a nie na wielu obiektach z mniej lub bardziej złożoną strukturą powiązań. Jak widać z analizy poniższego kodu funkcja <i>main</i> została zredukowana do minimum, a cała logika aplikacji została przeniesiona do klasy <i>MainApp</i>. Klasa ta nie powinna być wielokrotnie tworzona, dlatego została ona skonstruowana w oparciu o wzorzec S<i>ingelton</i>, który tego pilnuje.<br />
<br />
Polecam teraz dokładnie przeanalizować podany szablon, a dalsze wyjaśnienia będzie można przeczytać poniżej.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
//MainApp singelton class
class MainApp
{
private:
Mat canvas; // Image for drawing
Scalar bgr_color; // Background color
int x, y, speedX, speedY; // Circle local params
public: // Some global params:
static int DELAY;
static int CANVAS_WIDTH;
static int CANVAS_HEIGHT;
private:
MainApp() {}
MainApp(const MainApp &);
MainApp& operator=(const MainApp&);
// Initial commands for setup processing
void setup()
{
x = 0;
y = 0;
speedX = 1;
speedY = 1;
bgr_color = Scalar(120,235,139);
canvas = Mat(CANVAS_HEIGHT, CANVAS_WIDTH, CV_8UC3, bgr_color);
}
// Commands to modify the parameters
void update()
{
if(x < 0 ){
x = 0;
speedX *= -1;
} else if(x > CANVAS_WIDTH){
x = CANVAS_WIDTH;
speedX *= -1;
}
if(y < 0 ){
y = 0;
speedY *= -1;
} else if(y > CANVAS_HEIGHT){
y = CANVAS_HEIGHT;
speedY *= -1;
}
x+=speedX;
y+=speedY;
}
// Drawing functions:
void draw() {
canvas = bgr_color;
circle(canvas, Point(x,y), 30, Scalar(0,255,255),-1,CV_AA);
}
public:
static MainApp& getInstance()
{
static MainApp instance;
return instance;
}
// Main loop function with displaying image support
// and handle mouse and keyboard events
void run() {
setup();
const char *win_canvas = "Canvas";
namedWindow(win_canvas, CV_WINDOW_AUTOSIZE);
while (cvWaitKey(4) == -1) {
update();
draw();
imshow(win_canvas, canvas);
waitKey(DELAY);
}
}
};
// Best place to initialize global MainApp params:
int MainApp::DELAY = 5;
int MainApp::CANVAS_WIDTH = 500;
int MainApp::CANVAS_HEIGHT= 350;
int main(int, char**)
{
MainApp::getInstance().run();
return 0;
}</pre><br />
Patrząc na powyższy kod nie trudno zauważyć, że został on podzielony na kilka części. Dotyczy to szczególnie klasy <i>MainApp</i>. Została ona podzielona na funkcje, które mają ściśle określone role:<br />
<ul><li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>run()</b></span> - publicznie dostępna funkcja, która obsługuje główną pętlę programu. Program będzie się w niej wykonywał do momentu, aż użytkownik wciśnie klawisz <i>Escape</i>. Funkcja ta nie tylko wywołuje kolejne, niżej opisane funkcje, ale również odpowiada za wyświetlanie głównego okienka programu i obsługę zdarzeń nadchodzących z klawiatury i myszki.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>setup()</b></span> - funkcja wywoływana tylko raz, na początku działania programu, której zadaniem jest inicjalizacja zmiennych lokalnych i utworzenie obrazka, na którym będą rysowane pozostałe elementy.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>update()</b></span> - funkcja modyfikująca wybrane parametry działania programu. Tak naprawdę to tutaj powinna być zapisana główna logika działania programu. </li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>draw()</b></span> - zawiera wszystkie operacje rysowania na głównej scenie programu.</li>
</ul>Aby jeszcze dokładniej prześledzić organizację kodu w podanym szablonie przeanalizujmy przykład, który jest do niego dołączony. Mi wygodnie będzie to zrobić za pomocą następującego dialogu:<br />
<br />
<b>A.</b> Tych funkcji jest kilka. Kodu samego nie ma wiele. Czy robi on coś konkretnego?<br />
<br />
<b>B.</b> Wszystko co możemy zobaczyć na ekranie odbywa się w funkcji <i>draw()</i>. W tym przykładzie widać wyraźnie, że rysuje ona okręgi o promieniu 30 w punkcie o współrzędnych x, y.<br />
<br />
<b>A.</b> Nie wydaje się to nic szczególnie ciekawego.<br />
<br />
<b>B.</b> Warto teraz zajrzeć do funkcji <i>update()</i>. To tutaj znajduje się właściwe centrum sterowania aplikacji. To tutaj iteracyjnie modyfikowane są wybrane parametry elementów, które są wyświetlane na ekranie. W przypadku okręgu, o którym mowa, własnie w tej funkcji jest zaprogramowane, aby poruszał się on w linii prostej i odbijał się od krawędzi. <br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TZ4WgkeA4NI/AAAAAAAAGAs/pe5Lt3uV7SE/OpenCv-interactive_app_tutorial_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="350" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TZ4WgkeA4NI/AAAAAAAAGAs/pe5Lt3uV7SE/OpenCv-interactive_app_tutorial_1.png" width="500" /></a></div><br />
Domyślam się, że przedstawiony szablon nie pokazuje nic szczególnie odkrywczego ;-). Wielu zaawansowanych programistów <i>OpenCV</i>, do których ja się raczej nie zaliczam, pewnie stosuje podobne i lepsze rozwiązania od dawna. Mam jedynie nadzieje, że okaże się on przydatny dla osób, które dopiero zaczynają przygodę z <i>OpenCV</i> i chcą napisać swój pierwszy interaktywny program.<br />
<br />
Czekam oczywiście na uwagi i komentarze co można poprawić i co można zrobić lepiej.<br />
<br />
<b>Aktualizacja 2011.04.11</b>: Opisany w tym wpisie szkielet aplikacji <i>OpenCV</i> ma kilka niedoskonałości. Opisałem je we wpisie <i><a href="http://rpetryniak.blogspot.com/2011/04/poprawiony-szkielet-aplikacji.html">Poprawiony szkielet aplikacji interaktywnej dla OpenCV</a></i>. Zaproponowałem tam również jego udoskonaloną wersję. Szablon opisany w tym wpisie może być jednak z powodzeniem stosowany do prostszych aplikacji.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com2tag:blogger.com,1999:blog-19722740.post-42310405563860494232011-04-06T00:15:00.001+02:002014-04-06T18:43:20.154+02:00Detekcja ruchu w OpenCV - porównanie klatek<div dir="ltr" style="text-align: left;" trbidi="on">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/MotionDetection" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/logo/github-logo1.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/MotionDetection">Pełny kod źródłowy</a></td></tr>
</tbody></table>
<i>OpenCV</i> zostało stworzone i jest głównie używane tam, gdzie jest potrzebna zaawansowana analiza sekwencji wideo. Jest to narzędzie, które zawiera nie tylko funkcje obsługujące pobieranie obrazu z kamery, ale również funkcje, które potrafią ten obraz przetwarzać i wydobywać z niego ważne dla nas informacje. W przypadku analizy sekwencji wideo jednym z ważniejszych zadań jest detekcja miejsc, gdzie wystąpił ruch. Sama detekcja nie musi być szczególnie problematyczna, jeśli chodzi nam jedynie o proste wskazanie obszarów, na których coś się rusza. Wystarczy jedynie sprawdzić, które piksele na kolejnych klatkach obrazu zmieniają swoją wartość i podjąć decyzję na ile ta zmiana powinna być istotna, aby wykryć rzeczywisty ruch obiektu, a nie np. zaburzenia oświetlenia, bądź też ruch cienia obiektu. Oczywiście istnieje wiele problemów, które wymagają bardziej zaawansowanych algorytmów, aniżeli proste porównywanie obrazów. Możemy do nich mi.in zaliczyć rozpoznawanie i śledzenie wybranych obiektów.<br />
<br />
W dzisiejszym wpisie chciałem pokazać proste rozwiązanie problemu detekcji ruchu z wykorzystaniem biblioteki <i>OpenCV</i>. Skorzystam tutaj z opisanej powyżej koncepcji sprawdzania, które punkty obrazu zmieniają swoją wartość na kolejnych obrazach w sekwencji wideo. Najłatwiej w tym celu skorzystać z operacji odejmowania obrazów, a następnie binaryzacji obrazu wynikowego w celu określenia, jaka zmiana wartości pikseli powinna zostać uznana za ruch obiektu, a jaka za brak ruchu. Najczęściej stosuje się tutaj jedną z dwoch strategii odejmowania obrazów:<br />
<ul>
<li>odejmowanie dwóch sąsiednich obrazów, tzn. odejmowanie od bieżącej klatki, klatki poprzedniej,</li>
<li>odejmowanie od bieżącej klatki, pierwszej klatki, która została zarejestrowana i która jest uznawana jako tło (zakładamy oczywiście, że na pierwszej klatce nie ma obiektów, które później będą zmieniać swoje położenie).</li>
</ul>
Oba powyższe podejścia pomimo tego, że są bardzo zbliżone do siebie dają całkiem różne rezultaty. Pierwszy sposób wskazuje jedynie, na fragmenty obiektu, które zmieniły swoją lokalizację w sekwencji ruchu, podczas gdy drugi powinien wskazać cały obszar poruszającego się obiektu.<br />
<br />
Implementacja opisanych metod w <i>OpenCV</i> nie nastręcza wiele problemów. Obydwa sposoby mogą być zaimplementowane z pomocą poniższego fragmentu kodu:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">//TESTED WITH OpenCV 2.2
int diffType = atoi( argv[1] );
int thresval = atoi( argv[2] );
Mat cam_frame, img_gray, img_prev, img_diff, img_bin;
bool first_frame = true;
// LOOP FOR GRABBING IMAGE FROM WEBCAM
cap >> cam_frame;
cvtColor(cam_frame, img_gray, CV_BGR2GRAY);
if (first_frame) {
img_prev=img_gray.clone();
first_frame = false;
continue;
}
absdiff(img_gray, img_prev, img_diff);
threshold(img_diff, img_bin, thresval, 255, THRESH_BINARY);
erode(img_bin, img_bin, Mat(), Point(-1,-1), 3);
dilate(img_bin, img_bin, Mat(), Point(-1,-1), 1);
imshow(win_cam, cam_frame);
imshow(win_gray, img_gray);
imshow(win_diff, img_bin);
if (diffType == 1) img_prev=img_gray.clone();
// END LOOP</pre>
<br />
Analizując powyższy kod łatwo zauważyć, że jego działaniem możemy sterować za pomocą dwóch zmiennych:<br />
<ul>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>diffType</b></span> - określa, którą klatkę należy odejmować od klatki bieżącej, czy poprzednią (wartość = <i>1</i>), czy też pierwszą (wartość = <i>2</i>). To sprawdzanie odbywa się w ostatniej linijce powyższego kodu, gdzie jest ustawiana klatka, która będzie odejmowana.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>thresval</b></span> - zmienna ta będzie służyć do określania, co uznajemy za ruch obiektu, a co nie. Polecam ustawiać ją w przedziale od <i>30</i> do <i>100</i> (<i>60</i> powinno być w miarę bezpieczną, a zarazem skuteczną wartością progu).</li>
</ul>
Aby nie wchodzić w szczegółową analizę działania powyższego kodu, opiszę jedynie w skrócie jego najważniejsze fragmenty:<br />
<ul>
<li>W pierwszym przebiegu pętli zapamiętujemy jedynie pierwszą klatkę obrazu. Dzięki temu w drugiej iteracji będziemy mieli co odejmować od klatki bieżącej.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>absdiff</b></span> - bezwzględne odjęcie (pomijamy znak) dwóch klatek od siebie.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>threshold</b></span> - operacja progowania wykonywana na obrazie różnicowym. Pomijamy punkty, które nieznacznie zmieniły swoją wartość.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>erode</b></span> - usunięcie drobnych elementów, oraz wygładzenie krawędzi tych większych. Skutkiem ubocznym tej operacji jest zmniejszenie rozmiaru wszystkich obiektów.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>dilate</b></span> - dylatację stosujemy po etapie erozji, aby delikatnie powiększyć obiekty, tak aby miały podobny rozmiar jak to było przed erozją. </li>
</ul>
<div style="text-align: center;">
</div>
</div>
Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com4tag:blogger.com,1999:blog-19722740.post-62241341679603116842011-04-01T00:31:00.001+02:002011-04-01T13:09:27.375+02:00Obsługa parametrów wywołania programu w C++Pisząc programy konsolowe niejednokrotnie spotykamy się problemem przekazywania parametrów startowych. Najczęstszym rozwiązaniem jest ich wpisywanie zaraz po nazwie programu, dzięki czemu będą one później dostępne w tablicy <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>argv</b></span> funkcji <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>main</b></span>. Innym rozwiązaniem, rzadziej stosowanym jest ich zapisywanie do pliku tekstowego, np. XML i dalsze jego parsowanie zaraz po starcie programu. To drugie rozwiązanie zalecane jest raczej w przypadku naprawdę złożonych parametrów wywołania. W większości przypadków w zupełności wystarcza proste przekazywanie parametrów po nazwie programu w trakcie jego wywołania.<br />
<br />
Podejście klasyczne o przykładowym zapisie:<br />
<pre class="prettyprint lang-cpp notranslate">int zmienna1 = atoi (argv[1]);
int zmienna2 = atoi (argv[3]);</pre>może być kłopotliwe w stosowaniu, ponieważ wymaga dokładnego pamiętania kolejności poszczególnych parametrów. Znacznie lepszym rozwiązaniem są <u>parametry nazwane</u>, co do których ten wymóg nie jest już konieczny. Parametry tego typu znane są na pewno każdemu, kto jakiś czas pracował w konsoli tekstowej i potrzebował zmodyfikować domyślne parametry działania programu lub podać własne.<br />
<br />
Obecnie jest kilka mechanizmów obsługi tego typu parametrów w języku C++. Niestety nie są one wbudowane do biblioteki standardowej i wymagają stosowania zewnętrznych bibliotek, które mają nie tylko mocne, ale również słabe strony. Oto niektóre z nich:<br />
<ul><li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Boost.Program_options</span> - jeśli ktoś nie miał do tej pory potrzeby korzystania z bibliotek <i>Boost</i> to nie wydaje się dobrym pomysłem włączanie ich do projektu tylko ze względu na obsługę parametrów wejściowych. Bardziej szczegółowy opis biblioteki można znaleźć <a href="http://www.boost.org/doc/html/program_options.html">tutaj</a>.</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">GNU getopt</span> - użycie tego narzędzia w praktyce wymaga od użytkownika napisania kodu zawierającego instrukcję pętli <i>while</i> i przełącznik <i>switch</i>, przez co implementacja programu niepotrzebnie się wydłuża i może tracić czytelność (szczególnie w przypadku krótkich programów). Przykładowy program można zobaczyć w <a href="http://www.gnu.org/s/libc/manual/html_node/Example-of-Getopt.html">oficjalnej dokumentacji</a>. </li>
<li>Do innych rozwiązań tego typu można zaliczyć: <a href="http://code.google.com/p/google-gflags/">bibliotekę google-gflags</a>, <a href="http://opensource.dshevchenko.biz/clpp">bibliotekę CLPP</a>, <a href="http://artis.imag.fr/~Xavier.Decoret/resources/argstream/">argstream</a>, <a href="http://clp.sourceforge.net/">clp</a>.</li>
</ul>Każde z powyższych rozwiązań, nawet jeśli w praktyce działa bardzo dobrze, wymaga korzystania z dodatkowych bibliotek, co do których należy mieć pewność, że zostały poprawnie zainstalowane w systemie. Dodatkowo w przypadku, niedużych programów, nie wydaje się sensowne korzystanie z bibliotek, które swoim rozmiarem znacznie przewyższają kod implementowanego algorytmu. Ja osobiście nie przekonałem się do żadnej z powyższych bibliotek, dlatego postanowiłem sam zaprogramować obsługę parametrów wywołania programu.<br />
<br />
Poniższe rozwiązanie przygotowałem na podstawie przykładu ze strony: <a href="http://stackoverflow.com/questions/865668/parse-command-line-arguments/868894#868894">http://stackoverflow.com/questions/865668/parse-command-line-arguments/868894#868894</a>. Przykład ten niestety nie działał do końca poprawnie i ostateczne rozwiązanie udało mi sie przygotowac analizując kod biblioteki <i><a href="http://rpetryniak.blogspot.com/2008/07/cimg_19.html">CImg</a></i>, którą używam do przetwarzania obrazu, a która to miała zaimplementowany podobny mechanizm. Poniższego kodu nie będe szczegółowo opisywał. Mam nadzieje, że jest on wystarczająco czytelny :-).<br />
<br />
<b>Funkcja parsująca parametry wywołania programu w C++:</b><br />
<br />
<pre class="prettyprint lang-cpp notranslate">inline const char* getCmdOption(const char * name, const char * defaut, const int argc, const char *const * argv)
{
const char *res = 0;
if (argc > 0) {
int k = 0;
while (k < argc && strcmp(argv[k],name)) ++k;
res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
} else res = defaut;
return res;
}</pre><br />
<b>Przykładowe użycie funkcji <i>getCmdOption</i>:</b><br />
<br />
Funkcja <i>getCmdOption</i> już okazała mi się pomocna w kilku programach z zakresu przetwarzania obrazu. Poniżej fragment programu do wczytywania danych binarnych, ze szczególnym uwzględnieniem obsługi parametrów wejściowych:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">if (argc == 1) {
printf("Usage: %s -f filename.raw -dx [value] -dy [value] -dz [value]\n", argv[0]);
exit(0);
}
const char * filename = getCmdOption("-f", (char*)0, argc, argv );
const char * _dx = getCmdOption("-dx", (char*)0, argc, argv );
const char * _dy = getCmdOption("-dy", (char*)0, argc, argv );
const char * _dz = getCmdOption("-dz", (char*)0, argc, argv );
if ((char*)0 == filename) {
printf("Please specify input data file name (-f)\n");
exit(0);
}
if ((char*)0 == _dx || (char*)0 == _dx || (char*)0 == _dx) {
printf("Please specify input data dimension sizes (-dx, -dy, -dz)\n");
exit(0);
}
int dx = atoi( _dx );
int dy = atoi( _dy );
int dz = atoi( _dz );</pre><br />
To co było dla mnie najważniejsze i co mam nadzieje udało mi się uzyskać, to duża czytelność kodu. Mam nadzieje, że proponowana funkcja <i>getCmdOption</i>, okaże się również przydatna dla innych.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com6tag:blogger.com,1999:blog-19722740.post-58302968817902301802011-03-24T21:05:00.000+01:002011-04-01T13:13:09.811+02:00Wygodna konfiguracja procesu budowania programów OpenCV w CMakeO sposobach kompilacji programów <i>OpenCV</i> w <i>Linuksie</i> pisałem ostatnio we wpisie <i><a href="http://rpetryniak.blogspot.com/2011/03/opencv-instalacja-i-pierwszy-przykad-w.html">OpenCV - instalacja i pierwszy przykład w Ubuntu</a></i>. Pokazalem tam również przykladowy plik konfiguracyjny dla <i>CMake</i>: <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">CMakeLists.txt</span>. Na pierwszy rzut oka wydaje się on w porządku, jednakże jeśli zacznie nam przybywać nowych programów, wówczas kopiowanie tych dwóch linijek rozpoczynających się od <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ADD_EXECUTABLE</span> i <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">TARGET_LINK_LIBRARIES</span> może być irytujące. Można oczywiście sobie z tym poradzić i przekonać <i>CMake</i> d bardziej zautomatyzowanej pracy - przykład poniżej:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">PROJECT(NazwaProjektu)
cmake_minimum_required(VERSION 2.8)
FIND_PACKAGE( OpenCV REQUIRED )
SET(SOURCES
aplikacja1
aplikacja2
aplikacja3
aplikacja4
)
FOREACH(source ${SOURCES})
ADD_EXECUTABLE(${source} ${source}.cpp)
TARGET_LINK_LIBRARIES(${source} ${OpenCV_LIBS})
ENDFOREACH(source)</pre><br />
Jak łatwo zauważyć, dodawanie nowego programu do kompilacji nie wymaga wiele wysiłku. Wystarczy dodać nazwę pliku z kodem źródłowym w sekcji <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SOURCES</span> i gotowe.<br />
<br />
Życzę przyjemnej kompilacji programów <i>OpenCV</i> :-)Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-80241392520209108122011-03-22T22:57:00.002+01:002011-05-16T23:30:11.122+02:00Dostęp do składowych piksela w OpenCV 2.2<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/AssignPixel" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/logo/github-logo1.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/rpetryniak/OpenCv-Interactive-Apps-And-Examples/tree/master/AssignPixel">Pełny kod źródłowy</a></td></tr>
</tbody></table>Wraz ze zmianą wersji biblioteki <i>OpenCV</i> z <b><i>2.1</i></b> na <b><i>2.2</i></b> doszło do wielu zmian nie tylko w zakresie organizacji modułów, ale również w zakresie sposobu programowania. Dokładnie nie śledziłem jakie zaszły zmiany, więc nie będę ich dokładnie opisywał, jednak zauważyłem, że został położony większy nacisk na programowanie obiektowe. Widoczne jest to szczególnie w strukturach przechowujących obraz. Od zawsze do tego celu korzystało się ze struktury <i><b>IplImage</b></i>, natomiast teraz zalecane jest używanie klasy <i><b>cv::Mat</b></i>, która reprezentuje wielowymiarowe macierze danych. Aby szczegółowo zapoznać się z konstrukcją tej klasy najlepiej zajrzeć do oficjalnej dokumentacji pod hasło <a href="http://opencv.jp/opencv-2.2_org/cpp/core_basic_structures.html#mat"><i>Basic Structures → Mat</i></a>.<br />
<br />
Pierwszym problemem podczas przejścia ze struktury <i>IplImage</i> do używania klasy <i>Mat</i> będzie zapewne to, jak odczytać i zmodyfikować zawartość poszczególnych składowych piksela. Do tej pory było kilka sposobów, żeby sobie z tym poradzić (polecam wpis na oficjalnym <i>Wiki</i> pod hasłem: <a href="http://opencv.willowgarage.com/wiki/faq#Howtoaccessimagepixels"><i>How to access image pixels</i></a>). Teraz podobnie, mamy kilka możliwości, aby dostać się do piksela, jednak dokumentacja opisuje to bardzo pobieżnie. W rozdziale <i><a href="http://opencv.jp/opencv-2.2_org/cpp/fast_element_access.html">Introduction → Fast Element Access</a></i> zostały pokazane 3 różne sposoby operowania na pikselach obrazu, jednak dla mnie wydają się one kłopotliwe w użyciu. To właśnie było powodem przygotowania niniejszego poradnika, w którym chciałem przedstawić własne i znalezione w Internecie wygodne sposoby dostępu do składowych piksela.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Obrazy w skali szarości</span><br />
<br />
Na początek, aby nabrać wprawy najlepiej zrobić proste testy z wykorzystaniem obrazów 1-kanałowych. W tym celu obrazy pozyskane z kamery można przekonwertować na obraz szary i na nim wykonywać dalsze operacje.<br />
<br />
Poniżej został pokazany fragment kodu, który zawiera 3 sposoby dostępu do piksela, począwszy od tego najbardziej tradycyjnego, gdzie operujemy bezpośrednio na tablicy z danymi (<i>metoda 1</i>), poprzez użycie funkcji <b><i>at()</i></b>, której przekazujemy współrzędne interesującego nas punktu (<i>metoda 2</i>), a skończywszy na zastosowaniu wskaźnika do każdego analizowanego wiersza obrazu (<i>metoda 3</i>). Z tych 3 metod podejście obiektowe jest reprezentowane przez sposób 2, podczas gdy pozostałe 2 przypadki działają bardziej tradycyjnie.<br />
<br />
Aby skupić się na tym co istotne, poniższy kod zawiera jedynie najważniejszą część programu, z deklaracją używanych struktur reprezentujących obraz, oraz środkiem pętli przetwarzającej obraz z kamery. Aby uruchomić ten program można skorzystać z szablonu, który podałem w poprzednim wpisie <i><a href="http://rpetryniak.blogspot.com/2011/03/opencv-instalacja-i-pierwszy-przykad-w.html">OpenCV - instalacja i pierwszy przykład w Ubuntu</a></i> w sekcji <i>Ten sam przykład po nowemu</i>.<br />
<br />
<pre class="prettyprint lang-cpp notranslate">Mat cam_frame, img_gray_v1, img_gray_v2, img_gray_v3;
// LOOP FOR GRABBING IMAGE FROM WEBCAM
cap >> cam_frame;
// Method 1: Old style method operating on data array
cvtColor(cam_frame, img_gray_v1, CV_BGR2GRAY);
for(int i = 0; i < img_gray_v1.rows; i++)
for(int j = 0; j < img_gray_v1.cols; j++) {
img_gray_v1.data[img_gray_v1.step*i + j] = 255 - img_gray_v1.data[img_gray_v1.step*i + j];
}
// Method 2: Assign pixel using .at() function
cvtColor(cam_frame, img_gray_v2, CV_BGR2GRAY);
for(int i = 0; i < img_gray_v2.rows; i++)
for(int j = 0; j < img_gray_v2.cols; j++)
img_gray_v2.at<uchar>(i,j) = 255 - img_gray_v2.at<uchar>(i,j);
// Method2: Use plain C operator []. More efficient than method 2
// if you need to process a whole row of a 2d array
cvtColor(cam_frame, img_gray_v3, CV_BGR2GRAY);
for(int i = 0; i < img_gray_v3.rows; i++)
{
uchar* img_gray_v3_i = img_gray_v3.ptr<uchar>(i);
for(int j = 0; j < img_gray_v3.cols; j++)
img_gray_v3_i[j] = 255 - img_gray_v3_i[j];
}
// END LOOP</uchar></uchar></uchar></pre><br />
<span class="Apple-style-span" style="font-size: x-large;">Obrazy wielokanałowe</span><br />
<br />
Większy problem aniżeli obrazy 1-kanałowe sprawiają obrazy kolorowe, domyślnie uzyskiwane z tradycyjnych kamer cyfrowych. Należy tutaj pamiętać nie tylko o współrzędnych punktu, który jest analizowany, ale również o jego składowej koloru. na szczęście jest na to kilka sposobów, które można prześledzić poniżej.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Podejście tradycyjne</span><br />
<br />
Podobnie jak w przypadku obrazów szarych, dla obrazów wielokanałowych można również skorzystać z podejścia znanego z obróbki struktur <i>IplImage</i> i przeliczać wskaźnik do elementu, który nas interesuje. Przykład poniżej:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">Mat frame, result;
// LOOP FOR GRABBING IMAGE FROM WEBCAM
cap >> frame;
result = frame.clone();
// invert the image
for(i=0; i < frame.rows; i++)
for(j=0; j < frame.cols; j++)
for(k=0; k < channels; k++) {
uchar* temp_ptr = &((uchar*)(result.data + result.step*i))[j*3];
temp_ptr[0] = 255 - temp_ptr[0];
temp_ptr[1] = 255 - temp_ptr[1];
temp_ptr[2] = 255 - temp_ptr[2];
}
// END LOOP</pre><br />
<span class="Apple-style-span" style="font-size: large;">Sposoby opisane w oficjalnej dokumentacji</span><br />
<br />
Tak jak wspomniałem na początku tego wpisu, w oficjalnej dokumentacji we wpisie <i><a href="http://opencv.jp/opencv-2.2_org/cpp/fast_element_access.html">Introduction → Fast Element Access</a></i> pokazano 3 sposoby na to jak dostać się do składowych piksela. Dla mnie osobiście dziwne wydaje się rozbijanie obrazu wielokanałowego na obrazy zawierające jeden kanał i po wykonaniu obliczeń ponowne ich scalanie. Nie dość, że wydłuża to zapis algorytmu obróbki obrazu, to mam wrażenie, że powoduje dodatkowy narzut obliczeniowy. Kod z drobnymi poprawkami względem wersji oryginalnej znajduje się poniżej:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">Mat img_frame, img_resultA, img_resultB, img_resultC;
vector<mat> planesA,planesB,planesC;
// LOOP FOR GRABBING IMAGE FROM WEBCAM
cap >> img_frame;
// Method 1. process Y plane using an iterator
split(img_frame, planesA); // split the image into separate color planes
MatIterator_<uchar>
it1 = planesA[0].begin<uchar>(),
it1_end = planesA[0].end<uchar>(),
it2 = planesA[1].begin<uchar>(),
it3 = planesA[2].begin<uchar>();
for(; it1 != it1_end; ++it1,++it2,++it3 )
{
*it1 = 255 - *it1;
*it2 = 255 - *it2;
*it3 = 255 - *it3;
}
merge(planesA, img_resultA);
// Method 2. process the first chroma plane using pre-stored row pointer.
split(img_frame, planesB);
for( int y = 0; y < img_frame.rows; y++ )
{
uchar* Uptr1 = planesB[0].ptr<uchar>(y);
uchar* Uptr2 = planesB[1].ptr<uchar>(y);
uchar* Uptr3 = planesB[2].ptr<uchar>(y);
for( int x = 0; x < img_frame.cols; x++ )
{
Uptr1[x] = 255 - Uptr1[x];
Uptr2[x] = 255 - Uptr2[x];
Uptr3[x] = 255 - Uptr3[x];
}
}
merge(planesB, img_resultB);
// Method 3. process the second chroma plane using
// individual element access operations
split(img_frame, planesC);
for( int y = 0; y < img_frame.rows; y++ )
{
for( int x = 0; x < img_frame.cols; x++ )
{
uchar& Vxy1 = planesC[0].at<uchar>(y, x);
uchar& Vxy2 = planesC[1].at<uchar>(y, x);
uchar& Vxy3 = planesC[2].at<uchar>(y, x);
Vxy1 = 255 - Vxy1;
Vxy2 = 255 - Vxy2;
Vxy3 = 255 - Vxy3;
}
}
merge(planesC, img_resultC);
// END LOOP</uchar></uchar></uchar></uchar></uchar></uchar></uchar></uchar></uchar></uchar></uchar></mat></pre><br />
<span class="Apple-style-span" style="font-size: large;">Ostatnia deska ratunku</span><br />
<br />
Powyżej opisane sposoby dla obrazów wielokanałowych nie wydają się nazbyt wygodne i raczej zniechęcają, a nie zachęcają do stosowania klasy <i>Mat</i>. Na szczęście światełkiem w tunelu jest klasa pochodna po <i>cv::Mat</i> o niemal identycznej nazwie: <b><i>cv::Mat_</i></b>. Klasa ta ma opracowane operatory dostępu do składowych piksela poprzez zastosowanie konstrukcji <b>( x , y )</b>. Możemy w ten sposób uzyskać wskaźnik do konkretnego punktu obrazu, i dalej stosując zapis tablicowy <b>[]</b> możemy dostać się do konkretnej wartości danego kanału.<br />
<br />
<b>Przykład 1</b>: <u>Operowanie na oryginalnym obrazie:</u><br />
<br />
<pre class="prettyprint lang-cpp notranslate">Mat frame;
// LOOP FOR GRABBING IMAGE FROM WEBCAM
cap >> frame;
Mat_<vec3b>& frame_ = (Mat_<vec3b>&)frame;
for(int i = 0; i < frame_.rows; i++)
for(int j = 0; j < frame_.cols; j++)
for(int k = 0; k < 3; k++)
frame_(i,j)[k] = 255 - frame_(i,j)[k];
// END LOOP</vec3b></vec3b></pre><br />
<b>Przykład 2</b>: <u>Operowanie na obrazie pomocniczym:</u><br />
<br />
<pre class="prettyprint lang-cpp notranslate">Mat frame;
Mat_<vec3b> rgb;
// LOOP FOR GRABBING IMAGE FROM WEBCAM
cap >> frame;
cvtColor(frame, rgb, CV_BGR2RGB);
for(int i = 0; i < rgb.rows; i++)
for(int j = 0; j < rgb.cols; j++)
for(int k = 0; k < 3; k++)
rgb(i,j)[k] = 255 - rgb(i,j)[k];
// END LOOP</vec3b></pre><br />
Podsumowując zebrane w tym poradniku sposoby dostępu do składowych piksela, najwygodniejsze wydaje się użycie operatora <b><i>at()</i></b> dla obrazów jednokanałowych, natomiast dla obrazów wielokanałowych najprostszy i najbardziej czytelny zapis daje klasa <b><i>cv::Mat_</i></b>.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com2tag:blogger.com,1999:blog-19722740.post-73142760324718165322011-03-19T00:01:00.001+01:002011-03-19T09:30:33.412+01:00OpenCV - instalacja i pierwszy przykład w UbuntuJuż od dłuższego czasu przymierzałem się, aby więcej czasu poświęcić systemom interaktywnym, a w szczególności problematyce analizy ruchu. Zagadnieniami wizualizacji i przetwarzania obrazów medycznych oczywiście nie przestaje się dalej zajmować, jednakże opisywanie tej tematyki na blogu nie dość, że było i jest bardzo wymagające czasowo, to niestety interesuje tylko bardzo małą grupę osób. Natomiast analiza ruchu ma wiele różnych zastosowań - nie tylko tych naukowych, ale również rozrywkowych. Podobnie z punktu widzenia nauki przetwarzania informacji obrazowej, dla wielu młodych osób ciekawsze wydaje się analizowanie obrazu z kamerki na żywo, zamiast analizy statycznych obrazów medycznych.<br />
<br />
Tym wpisem rozpoczynam serię poradników dotyczących właśnie analizy ruchu. Głównym narzędziem (biblioteką) jak będzie używana będzie <i>OpenCV</i>, o którym <a href="http://rpetryniak.blogspot.com/2008/08/opencv_01.html">pisałem ponad 2 lata temu na tym blogu</a>. Dzisiaj chciałbym opisać proces instalacji tej biblioteki w <i>Ubuntu</i> i pokazać prosty przykład z użyciem kamerki internetowej.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Instalacja z repozytorium</span><br />
<br />
Aby zainstalować <i>OpenCV</i> z repozytorium wpisujemy następujące polecenie w okienku terminala:<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install libcv-dev libcvaux-dev libhighgui-dev</pre><br />
Patrząc na tą komendę można zauważyć, że instalowane są tylko pakiety programistyczne. A co z bibliotekami? Zainstalują się one oczywiście same jako zależności wyżej wymienionych pakietów. Takie podejście jest oczywiście bardziej uniwersalne w przypadku instalacji <i>OpenCV</i>, ponieważ biblioteki te mają dodaną końcówkę <b><i>2.1</i></b> (np. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">libcv2.1</span>), a tak nie będzie zawsze. Dla przykładu na <a href="http://opencv.willowgarage.com/wiki/">stronie projektu</a> jest już dostępna wersja <b><i>2.2</i></b>.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Instalacja ze źródeł</span><br />
<br />
Tak jak wspomniałem powyżej, wersja, która znajduje się w repozytorium nie jest najświeższa i osobom, którym szczególnie zależy, aby pracować na ostatniej wersji tej biblioteki polecam samodzielną kompilację ze źródeł. Nie jest ona skomplikowana, dlatego również zostanie opisana w tym poradniku.<br />
<br />
Najnowszą stabilną wersję źródeł <i>OpenCV</i> można pobrać z portalu <i>SourceForge</i>, a dokładnie ze strony: <a href="http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/">http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/</a>. Można pobrać ją również bezpośrednio z repozytorium, aby mieć dostęp do aktualnie wprowadzanych funkcji. W tym celu wystarczy wpisać w konsoli następujące polecenie:<br />
<pre class="prettyprint lang-bsh notranslate">svn co https://code.ros.org/svn/opencv/trunk</pre><br />
Ja osobiście polecam wersję stabilną. Obecnie jest to wersja <b><i>2.2</i></b>.<br />
<br />
Teraz kiedy mamy pobrane już źródła biblioteki <i>OpenCV</i>, możemy przystąpić do jej kompilacji. Wykonujemy w tym celu następujące kroki:<br />
<ul><li>Utworzenie katalogu <i>build</i> i uruchomienie w nim graficznej nakładki na <i>CMake</i>:<br />
<pre class="prettyprint lang-bsh notranslate">mkdir build
cd build
cmake-gui ../</pre></li>
<li>Przy pierwszej kompilacji najlepiej nie ustawiać zbyt wiele opcji, aby mieć pewność że podstawowa wersja się skompiluje. Ja ustawiłem tylko typ kompilacji na „<i>Release</i>”<br />
<pre class="prettyprint lang-bsh notranslate">CMAKE_BUILD_TYPE Release</pre></li>
<li>Uruchomienie kompilacji. Jeśli mamy wiecej niż jeden rdzeń warto z tego skorzystać stosując przełącznik <b><i>j</i></b>:<br />
<pre class="prettyprint lang-bsh notranslate">make -j2</pre></li>
<li>Jeśli wszystko dobrze się skompiluje można wówczas uruchomić kolejne przebiegi kompilacji z włączonymi następującymi opcjami:<br />
<pre class="prettyprint lang-bsh notranslate">BUILD_EXAMPLES ON
BUILD_NEW_PYTHON_SUPPORT ON
OPENCV_BUILD_3RDPARTY_LIBS ON</pre>Oczywiście tych opcji jest dużo więcej, dlatego polecam samodzielne testowanie pozostałych, chociaż te o których napisałem powinny w zupełności wystarczyć.<br />
</li>
</ul>Jeśli pojawiły się jakieś niespodziewane błędy podczas kompilacji polecam zajrzeć na stronę:<br />
<a href="http://opencv.willowgarage.com/wiki/InstallGuide">http://opencv.willowgarage.com/wiki/InstallGuide</a> w celu znalezienia rozwiązania.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Pierwsze testy</span><br />
<br />
Na początek warto przetestować przykłady załączone w katalogu s<i>amples</i>. Po kompilacji odpowiadające im wersje binarne powinny znaleźć się w katalogu: <i>build/bin/</i>. Ja sprawdziłem wszystkie i od razu mogę podpowiedzieć, że z kamerką działają te z nich:<br />
<ul><li>adaptiveskindetector</li>
<li>bgfg_codebook</li>
<li>bgfg_segm</li>
<li>camshiftdemo</li>
<li>fback</li>
<li>fback_c</li>
<li>laplace</li>
<li>lkdemo</li>
<li>motempl</li>
<li>segment_objects</li>
</ul>Pomimo tego, że nie są one szczególnie złożone, na początek warto zacząć od jeszcze prostszych przykładów.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Wyświetlanie obrazu z kamery</span><br />
<br />
Jednym z pierwszych przykładów, które warto przeanalizować i uruchomić w <i>OpenCV</i> jest pobieranie obrazu z kamery i wyświetlenie go na ekranie monitora. Nie jest to oczywiście trudne, ale wymaga znajomości kilku elementarnych funkcji takich jak:<br />
<ul><li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">cvCaptureFromCAM</span> - inicjalizacja kamery,</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">cvQueryFrame</span> - pobranie pojedynczej klatki obrazu,</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">cvShowImage</span> - wyświetlenie obrazka we wskazanym oknie.</li>
</ul>Cały przykład wygląda następująco:<br />
<pre class="prettyprint lang-cpp notranslate">#include "opencv/highgui.h"
int main()
{
CvCapture *cam = cvCaptureFromCAM(-1);
const char *window = "Example 1";
cvNamedWindow(window, CV_WINDOW_AUTOSIZE);
while (cvWaitKey(4) == -1) {
IplImage *frame = cvQueryFrame(cam);
cvShowImage(window, frame);
}
cvDestroyAllWindows();
cvReleaseCapture(&cam);
return 0;
}</pre><br />
Aby go skompilować wystarczy uruchomić:<br />
<pre class="prettyprint lang-bsh notranslate">gcc camera1.cpp -o camera1 -lhighgui</pre><br />
<span class="Apple-style-span" style="font-size: large;">Ten sam przykład po nowemu</span><br />
<br />
Przykład przedstawiony powyżej działa zarówno w bieżącej wersji <i>OpenCV</i> jak i w wersjach poprzednich. Jednakże własnie od wersji bieżącej <b><i>2.2</i></b> biblioteka ta została uporządkowana i podzielona na moduły. Dodatkowo zostały wprowadzone nowe funkcje i został zaproponowany bardziej obiektowy model programowania. Wszystkie te zmiany mają za zadanie ułatwić programowanie, ale nie pozwalają już na kompilację nowych programów tam gdzie jest stara wersja biblioteki. Program po zmianach wg nowych zaleceń wygląda tak:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main(int, char**)
{
VideoCapture cap(0);
if( !cap.isOpened() ) return -1;
Mat frame;
const char *window = "Example 2";
namedWindow(window, CV_WINDOW_AUTOSIZE);
while (cvWaitKey(4) == -1) {
cap >> frame;
imshow(window, frame);
}
return 0;
}</pre><br />
Kompilacja tak napisanego programu w konsoli nie jest już taka trywialna jak poprzednio. W takiej sytuacji najlepiej będzie skorzystać z narzędzia <i>CMake</i>, które jest podstawą budowania całej biblioteki <i>OpenCV</i>. W katalogu ze źródłami omawianych aplikacji tworzymy nowy plik <i>CMakeLists.txt</i>. Powinien on zawierać taką treść:<br />
<br />
<pre class="prettyprint lang-cpp notranslate">PROJECT(opencv_example)
cmake_minimum_required(VERSION 2.8)
FIND_PACKAGE( OpenCV REQUIRED )
# Declare the target (an executable)
ADD_EXECUTABLE(camera1 camera1.cpp)
TARGET_LINK_LIBRARIES(camera1 ${OpenCV_LIBS})
ADD_EXECUTABLE(camera2 camera2.cpp)
TARGET_LINK_LIBRARIES(camera2 ${OpenCV_LIBS})</pre><br />
Teraz już kompilacja sprowadza się do wpisania w konsoli:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">mkdir build
cd build
cmake-gui ../</pre><br />
i określenia w parametrach <i>CMake</i> ścieżki do katalogu gdzie mamy zbudowany <i>OpenCV</i>. U mnie wygląda to tak:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">OpenCV_DIR /home/rafal/installed/opencv/OpenCV-2.2.0/build</pre><br />
Teraz już wystarczy wpisać <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>make</b></span> i oba programy zostaną skompilowane.<br />
<br />
W ten sposób udało nam się przygotować dwie aplikacje, które niby nie robią wiele, ale stanowią szkielet większości aplikacji interaktywnych w <i>OpenCV</i>.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com6tag:blogger.com,1999:blog-19722740.post-9833119344509678632011-03-03T00:13:00.001+01:002011-03-04T23:52:41.423+01:00Nie do końca udana wirtualizacja MeeGo w VirtualBoxieNa początek od razu wyjasnię o co chodzi w tytule, bo wiem, że może on nie zachęcać do czytania dalej. Przez ostatnie kilka dni, mając trochę wolnego czasu postanowiłem przetestować pełną wersję <i>MeeGo Netbook</i> w środowisku wirtualnym z którego zawsze korzystam, czyli w <i>VirtualBoxie</i>. Skłoniło mnie do tego nie tylko to, że wersja dostępna w emulatorze <i>QEMU</i> (jak zainstalować środowisko uruchomieniowe pisałem we wpisie <a href="http://rpetryniak.blogspot.com/2011/02/meego-pierwsze-starcie.html">MeeGo - pierwsze starcie</a>) jest mocno okrojona, ale również to, że działa ona dosyć powoli. Miałem nadzieję, że w <i>VirtualBoxie</i> będzie inaczej, bo to w końcu uznana marka wśród narzędzi do wirtualizacji. Sama instalacja pomimo tego, że nie obyła się bez problemów zakończyła się sukcesem. Niestety problem z powolnym działaniem systemu i brakiem jego responsywności również tutaj był mocno widoczny. Próbowałem różnych sposobów i ostatecznie żaden nie pomógł. Nawet deweloperzy <i>MeeGo</i> sami twierdzą, że z <i>VirtualBoxem</i> mogą być problemy i w miarę możliwości zachęcają do instalacji systemu na kompatybilnych urządzeniach. <br />
<br />
Pomimo tego, że <i>MeeGo</i> w <i>VirtualBoxie</i> nie działa zachwycająco, to dla osób, które nie mają odpowiedniego sprzętu do dyspozycji lub nie chcą instalować kolejnego systemu jedynie w celach testowych na maszynie, na której pracują na co dzień, myślę, że zastosowanie <i>VirtualBoxa</i> może być dobrym rozwiązaniem. W tym wpisie nie będę szczegółowo rozpisywał się na jakie problemy napotkałem podczas instalacji <i>MeeGo</i> w <i>VirtualBoxie</i>, a jedynie przedstawię niezbędne kroki, które pozwolą na instalację i uruchomienie systemu.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Pobranie obrazu instalacyjnego</span><br />
<br />
Płytę z obrazem instalacyjnym <i>MeeGo Netbook</i> pobieramy ze strony: <a href="http://meego.com/downloads/releases/netbook">http://meego.com/downloads/releases/netbook</a>. Ponieważ <i>VirtualBox</i> nie widzi płyt <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>img</b></span> możemy bez żadnego problemu zmienić rozszerzenie pobranego pliku na <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>iso</b></span>.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Utworzenie nowej maszyny wirtualnej</span><br />
<br />
Dalej należy utworzyć pustą maszynę wirtualną. W kreatorze <i>VirtualBoxa</i>, który do tego służy możemy podać następujące parametry:<br />
<ul><li>nazwa: <b>MeeGo Netbook v1.1</b></li>
<li>typ systemu: <b>Linux/Fedora</b></li>
<li>pamięć: <b>512MB</b></li>
<li>dysk: <b>dynamicznie rozszerzalny, 8GB</b></li>
</ul>Dodatkowo samodzielnie należy wejść do opcji i ustawić parametry jak poniżej:<br />
<ul><li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">System</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Procesor</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Włącz PAE/NX</span></li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ekran</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Wideo</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Włącz akcelerację 3D</span></li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Nośniki</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">kontroler IDE</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Choose a virtual CD disc file</span> (wskazujemy na wcześniej pobrany plik z obrazem).</li>
</ul><span class="Apple-style-span" style="font-size: large;">Instalacja MeeGo NetBook</span><br />
<br />
Po uruchomieniu systemu wirtualnego powinno nam się pojawić okienko jak poniżej:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TW4BdVfZjnI/AAAAAAAAF40/maUvTCjQcL0/meego-virtualbox_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TW4BdVfZjnI/AAAAAAAAF40/maUvTCjQcL0/meego-virtualbox_1.png" width="480" /></a></div><br />
Wybieramy na nim opcję "<i>Instalation only</i>". Po chwili czekania powinna się rozpocząć instalacja systemu. Wita nas ona takim obrazkiem:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TW4BdpDIzZI/AAAAAAAAF44/-95S9BJaEFI/meego-virtualbox_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TW4BdpDIzZI/AAAAAAAAF44/-95S9BJaEFI/meego-virtualbox_2.png" width="480" /></a></div><br />
Na kolejnych ekranach wystarczy jedynie ustawić język i skonfigurować dysk. W przypadku pustego dysku wirtualnego nie jest to zbytnio skomplikowane. Potwierdzamy jedynie komunikaty, które się pojawią. O zakończeniu instalacji zostaniemy powiadomieni obrazem jak poniżej:<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh4.googleusercontent.com/_AeFiJN9phoM/TW4BeI21T5I/AAAAAAAAF5Q/0q8YAuBqMnk/meego-virtualbox_8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh4.googleusercontent.com/_AeFiJN9phoM/TW4BeI21T5I/AAAAAAAAF5Q/0q8YAuBqMnk/meego-virtualbox_8.png" width="480" /></a></div>Kliknięcie na przycisk <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Zamknij</span> spowoduje restart maszyny wirtualnej. Warto ją w tym momencie w ogóle zatrzymać i odmontować z napędu CD płytę instalacyjną.<br />
<br />
Po ponownym uruchomieniu systemu powinniśmy zobaczyć kreator pierwszego uruchomienia. Pyta nas on o ustawienia klawiatury, strefę czasową i prosi o utworzenie użytkownika.<br />
<br />
Jeśli system teraz wystartuje to mamy szczęście :-) Ja tego szczęścia niestety nie miałem i zobaczyłem czarny ekran. Po ponownym restarcie komputera wyświetlił mi się tylko obraz tła.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Problem przy pierwszym uruchomieniu</span><br />
<br />
Przy rozwiązywaniu problemów, które zaczęły się pojawiać bardzo pomocna okazała się ta strona na <i>Wiki MeeGo</i>: <a href="http://wiki.meego.com/MeeGo_1.0_Netbook_VirtualBox">http://wiki.meego.com/MeeGo_1.0_Netbook_VirtualBox</a>. Korzystając z porad tam zawartych wykonałem następujące czynności:<br />
<ul><li>Resetujemy maszynę wirtualną i zanim zacznie startować system wciskamy <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Escape</span>, a następnie <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Tab</span>.</li>
<li>Pojawi nam się linijka z tekstem jak na obrazku poniżej, którą możemy wyedytować. Kasujemy tutaj słowo '<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">quiet</span>' i dodajemy literkę '<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">s</span>'.</li>
</ul><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/_AeFiJN9phoM/TW4Be1rRO7I/AAAAAAAAF5o/kWmCEbD26Xc/meego-virtualbox_15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh3.googleusercontent.com/_AeFiJN9phoM/TW4Be1rRO7I/AAAAAAAAF5o/kWmCEbD26Xc/meego-virtualbox_15.png" width="480" /></a></div><ul><li>Klikamy <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Enter</span> i czekamy. Po chwili powinniśmy zobaczyć wiersz poleceń ze znaczkiem zachęty symbolizującym tryb administracyjny. Skoro już tutaj jesteśmy warto ustawić hasło roota (polecenie '<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">passwd</span>') i zainstalować 2 przydatne narzędzia (<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">wget</span> i <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">nano</span>), które mogą okazać się niezbędne na kolejnych etapach konfiguracji.<br />
<pre class="prettyprint lang-cpp notranslate">zypper install wget nano</pre></li>
<li>Następnie w konsoli wpisujemy następujące polecenie:<br />
<pre class="prettyprint lang-cpp notranslate">chmod +s /usr/bin/Xorg</pre></li>
<li>i restartujemy system:<br />
<pre class="prettyprint lang-cpp notranslate">reboot</pre></li>
</ul><div>Teraz system powinien wystartować, ale wcale nie ma takiej pewności :-) U mnie na jednym komputerze opisane podejście zadziałało, a na innym nie przyniosło żadnego rezultatu.<br />
<br />
Jeśli system ciągle nie startuje warto sprawdzić, czy nie występuje problem jak ten opisany na forum <i>MeeGo</i> w wątku: <a href="http://forum.meego.com/showthread.php?t=447">INIT: Id "x" respawning too fast: disabled</a>. U mnie właśnie tak było i polecam sprawdzić opisane tam rozwiązanie.</div><br />
Jeśli będziemy mieć szczęście (a na pewno w końcu się uda ;) ) to system powinien wystartować i przywitać nas ekranem jak ten poniżej:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TW4BfRBJ2iI/AAAAAAAAF50/GVRsPFHUz1c/meego-virtualbox_20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TW4BfRBJ2iI/AAAAAAAAF50/GVRsPFHUz1c/meego-virtualbox_20.png" width="480" /></a></div><br />
<span class="Apple-style-span" style="font-size: large;">Próba włączenia wsparcia OpenGL</span><br />
<br />
Niestety, tak jak wspomniałem na początku, <i>MeeGo</i> w podstawowej konfiguracji w <i>Virtualboxie</i> nie zachwyca szybkością - chodzi tak samo ociężale jak wersja dostępna na <i>QEMU</i>. Na <i>Wiki MeeGo</i> <a href="http://wiki.meego.com/MeeGo_1.0_Netbook_VirtualBox#Installing_VirtualBox_guest_additions_for_OpenGL_acceleration">jest rozwiązanie tego problemu</a>, ale przyznam szczerze, że u mnie nie działa. Testowałem to na kilka sposobów i albo nie było żadnego efektu, albo system nie mógł wystartować. Opiszę jednak kroki, które wykonałem, bo może u kogoś zadziała lub po prostu ktoś będzie w stanie podpowiedzieć mi co źle robiłem.<br />
<br />
Po kolei co należy zrobić, aby spróbować uruchomić akcelerację <i>OpenGl</i> w <i>MeeGo</i> zainstalowanego w <i>VirtualBox</i> (w skrócie):<br />
<ul><li>Uruchamiamy okienko terminala: zakładka Aplikacje → Narzędzia systemowe.</li>
<li>Instalujemy następujące pakiety i restartujemy system:<br />
<pre class="prettyprint lang-cpp notranslate">sudo zypper install gcc make kernel-netbook-devel patch nano wget yum yum-utils
sudo reboot</pre></li>
<li>Instalujemy dodatki <i>VirtualBoxa</i>. Przed wykonaniem poniższych czynności najpierw należy wybrać w opcjach maszyny: <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Urządzenia</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Zainstaluj dodatki</span>.<br />
<pre class="prettyprint lang-cpp notranslate">sudo mount /dev/sr0 /mnt
cd /mnt
sudo ./VBoxLinuxAdditions-x86.run
sudo reboot</pre></li>
<li>Tworzymy katalog tymczasowy i próbujemy pobrać źródła C<i>luttera</i> z repozytorium:<br />
<pre class="prettyprint lang-cpp notranslate">cd ~
mkdir tmp
cd tmp
yumdownloader --source clutter</pre></li>
<li><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">Niestety u mnie ta próba kończy się niepowodzeniem. Dostaję komunikat podobny do tego:</div><blockquote><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">Error: Cannot retrieve repository metadata (repomd.xml) for repository: meego-core. Please verify its path and try again </div></blockquote></li>
<li>Nie pozostaje nam zatem nic innego jak samodzielne pobranie odpowiedniego archiwum ze źródłami<br />
<pre class="prettyprint lang-cpp notranslate">wget http://repo.meego.com/MeeGo/releases/1.1/core/repos/source/clutter-1.2.8-2.131.src.rpm
rpm2cpio clutter-1.2.6-2.2.src.rpm|cpio -id
tar jvxf clutter-1.2.6.tar.bz2</pre></li>
<li>Następnie pobieramy zależności do zbudowania <i>Cluttera</i>. Można to zrobić instalując pakiet <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">clutter-devel</span>, co pozwoli na automatyczne dociągnięcie wszystkich zależnych pakietów: <br />
<pre class="prettyprint lang-cpp notranslate">sudo zypper install clutter-devel</pre></li>
<li>Ostatecznie uruchamiamy procedure budowania i instalacji <i>Cluttera</i>. Między czasie pobieramy i stosujemy dodatkowa łatkę z forum <i>MeeGo</i>.<br />
<pre class="prettyprint lang-cpp notranslate">cd clutter-1.2.8
wget http://wiki.meego.com/images/Clutter-xvisual-patch.txt
patch -p1 < ./Clutter-xvisual-patch.txt
./configure --prefix=/usr
make
sudo make install
reboot</pre></li>
</ul>Po wykonaniu opisanych czynności restartujemy komputer. Należ się również upewnić, że jest włączona akceleracja 3D w ustawieniach <i>VirtualBoxa</i>.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Prezentacja systemu</span><br />
<br />
Na koniec wpisu załączam małą galerię slajdów wykonanych nie tylko na kolejnych etapach instalacji i konfiguracji <i>MeeGo</i> w <i>Virtualboxie</i>, ale również slajdy prezentujące wygląd poszczególnych ekranów systemu.<br />
<br />
<div style="font-family: arial,sans-serif; font-size: 13px; width: 600px;"><div><embed flashvars="host=picasaweb.google.com&noautoplay=1&hl=pl&feat=flashalbum&RGB=0x000000&feed=https%3A%2F%2Fpicasaweb.google.com%2Fdata%2Ffeed%2Fapi%2Fuser%2Frpetryniak%2Falbumid%2F5579398298530844945%3Falt%3Drss%26kind%3Dphoto%26hl%3Dpl" height="400" pluginspage="http://www.macromedia.com/go/getflashplayer" src="https://picasaweb.google.com/s/c/bin/slideshow.swf" type="application/x-shockwave-flash" width="600"></embed></div><span style="float: left;"><a href="https://picasaweb.google.com/rpetryniak/MeeGoWVirtualBox?feat=flashalbum" style="color: #3964c2;">Wyświetl wszystkie</a></span><br />
<div style="text-align: right;"><a href="http://picasaweb.google.com/lh/getEmbed?feat=flashalbum" style="color: #3964c2;">Uzyskaj własny</a></div></div>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com2tag:blogger.com,1999:blog-19722740.post-31639092638036247682011-02-25T00:19:00.001+01:002011-03-04T23:52:41.424+01:00Pierwsza mobilna aplikacja dla MeeGoWitam. Podobnie jak w przypadku poprzednich dwóch postów, dzisiaj również ostrzegam :-), że nie będzie nic odkrywczego względem <a href="http://wiki.meego.com/SDK/Docs/1.1">oficjalnego Wiki dla programistów MeeGo</a>. Pomimo tego, że sporo korzystam z materiałów tam zawartych, to moje wpisy nie są ich tłumaczeniami, ale raczej opisem własnych doświadczeń z testowania dostępnych tam poradników.<br />
<br />
Tym razem będzie o tym jak napisać, skompilować i uruchomić swoją pierwszą aplikacje mobilną dla <i>MeeGo</i>. Wpis ten jest mocno inspirowany następującą stroną na <i>Wiki MeeGo</i>:<br />
<a href="http://wiki.meego.com/SDK/Docs/1.1/Creating_Hello_World">http://wiki.meego.com/SDK/Docs/1.1/Creating_Hello_World</a>.<br />
<br />
Zakładam na początek, że mamy już <a href="http://rpetryniak.blogspot.com/2011/02/meego-pierwsze-starcie.html">zainstalowane środowisko wdrożeniowe i uruchomieniowe</a> oraz, że została przeprowadzona wymagana <a href="http://rpetryniak.blogspot.com/2011/02/meego-sdk-konfiguracja-qt-creator.html">konfiguracja Qt Creatora</a>.<br />
<br />
W pierwszym momencie - zapewne jak się nie trudno domyśleć, należy uruchomić <i>Qt Creatora</i>. W następnym kroku przystępujemy do stworzenia nowego projektu wybierając odpowiednio w menu <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Plik</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Nowy plik lub projekt</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Mobilna aplikacja Qt</span>. Potwierdzamy klikając <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Wybierz…</span> .<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TWbN_4a_ojI/AAAAAAAAF2s/sgNGaafQk70/meego-pierwsza_aplikacja_w_qt_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TWbN_4a_ojI/AAAAAAAAF2s/sgNGaafQk70/meego-pierwsza_aplikacja_w_qt_1.png" width="430" /></a></div><br />
Określamy następnie nazwę projektu i wybieramy jego docelową lokalizację.<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWbOANbvgFI/AAAAAAAAF24/tTbhJbclVX8/meego-pierwsza_aplikacja_w_qt_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWbOANbvgFI/AAAAAAAAF24/tTbhJbclVX8/meego-pierwsza_aplikacja_w_qt_2.png" width="430" /></a></div><br />
W kolejnym kroku wybieramy jako wersję <i>Qt</i> tą dostępną po instalacji <i>MeeGo SDK</i>.<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWbN_-eukiI/AAAAAAAAF2w/vtvG0SfyQ78/meego-pierwsza_aplikacja_w_qt_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWbN_-eukiI/AAAAAAAAF2w/vtvG0SfyQ78/meego-pierwsza_aplikacja_w_qt_3.png" width="430" /></a></div><br />
Możemy również zmienić domyślne nazwy plików, ale skoro ma to być tylko aplikacja testowa polecam pozostawienie tych zaproponowanych przez kreatora.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWbOAGmDtWI/AAAAAAAAF20/Hsjufe_fsFY/meego-pierwsza_aplikacja_w_qt_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWbOAGmDtWI/AAAAAAAAF20/Hsjufe_fsFY/meego-pierwsza_aplikacja_w_qt_4.png" width="430" /></a></div><br />
Ostatecznie dostajemy ogólne podsumowanie nowo utworzonego projektu:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TWbfV1s_SiI/AAAAAAAAF30/1hzEuPmkpeU/meego-pierwsza_aplikacja_w_qt_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TWbfV1s_SiI/AAAAAAAAF30/1hzEuPmkpeU/meego-pierwsza_aplikacja_w_qt_5.png" width="430" /></a></div><br />
Prawdopodobnie <i>Qt Creator</i> otworzy się w trybie projektowania interfejsu, dlatego przełączamy się do trybu <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Edycji</span> kodu źródłowego. Posługując się nawigatorem projektu otwieramy plik <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">main.cpp</span> i zamieniamy jego zawartość na następującą:<br />
<pre class="prettyprint lang-cpp notranslate">#include <qapplication>
#include <qlabel>
#include <qsysteminfo>
using namespace QtMobility;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSystemInfo s;
QLabel *label = new QLabel(QObject::tr("hello").
append(s.currentCountryCode()));
label->show();
label->resize(100,30);
return app.exec();
}</pre><br />
Teraz należy wyedytować plik z ustawieniami projektu <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">*.pro</span> dodając do niego następujące wpisy:<br />
<pre class="prettyprint lang-bsh notranslate">CONFIG += mobility
MOBILITY += systeminfo</pre><br />
Aby zapisać wszystkie wprowadzone zmiany najlepiej skorzystać z menu <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Plik</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Zachowaj wszystko</span>.<br />
<br />
Przed uruchomieniem przygotowanej aplikacji należy najpierw przejść do zakładki <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Projekty</span> i tam na zakładce <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Produkty docelowe</span> wybrać <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">MeeGo</span> i <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Uruchom</span>.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWbOAUMysJI/AAAAAAAAF28/wcsr2cv_90o/meego-pierwsza_aplikacja_w_qt_6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWbOAUMysJI/AAAAAAAAF28/wcsr2cv_90o/meego-pierwsza_aplikacja_w_qt_6.png" width="500" /></a></div><br />
Tutaj należy określić jedynie <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Device configuration</span>, gdzie powinniśmy wskazać na skonfigurowane wcześniej środowisko uruchomieniowe (jak to zrobić pisałem we wpisie "<a href="http://rpetryniak.blogspot.com/2011/02/meego-sdk-konfiguracja-qt-creator.html">MeeGo SDK - Konfiguracja Qt Creator</a>", w sekcji "<i>Dostęp do środowiska uruchomieniowego MeeGo</i>"). U mnie to jest akurat: <b>MeeGo NetBook Emulator</b>. Oczywiście <u>należy pamiętać o tym, aby wystartować wybrane środowisko uruchomieniowe <i>MeeGo</i></u>. Możemy to zrobić wpisując w konsoli polecenie tego typu:<br />
<pre class="prettyprint lang-bsh notranslate">sudo mad remote -r meego-netbook-ia32-qemu-1.1.2-runtime poweron</pre><br />
Po tym wszystkim możemy uruchomić projekt klikając na zielony "trójkącik" symbolizujący start aplikacji. Prawdopodobnie w konsoli <i>Qt Creatora</i> zobaczymy komunikat podobny do tego:<br />
<blockquote>Cleaning up remote leftovers first ...<br />
Initial cleanup done.<br />
Files to deploy: /home/rafal/qthello-build-meego/rrpmbuild/qthello-0.0.1-1.i586.rpm.<br />
Deployment finished.<br />
Starting remote application.<br />
access control disabled, clients can connect from any host<br />
Could not find Hal<br />
Could not find Hal</blockquote>Teoretycznie teraz powinniśmy zobaczyć w testowym środowisku <i>MeeGo</i> wystartowaną aplikację. Ja niestety na pierwszy rzut oka nie dostrzegłem żadnych zmian. Po kilku kliknięciach w interfejs zauważyłem, że aplikacja jest jednak uruchomiona, ale znajduje się "w tle". Aby zobaczyć ją na pełnym ekranie wystarczy kliknąć na pierwszą zakładkę - tą z domkiem, symbolizującą strefę <i>MyZones</i> (widać to na rysunku poniżej).<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWbOZs5PBCI/AAAAAAAAF3U/RKvxbEtER8c/meego-pierwsza_aplikacja_w_qt_7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWbOZs5PBCI/AAAAAAAAF3U/RKvxbEtER8c/meego-pierwsza_aplikacja_w_qt_7.png" /></a></div><br />
Teraz już powinniśmy zobaczyć naszą pierwszą mobilną aplikację dla MeeGo :-D :<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWbOZlZvR5I/AAAAAAAAF3Y/N-RUbvWTOGA/meego-pierwsza_aplikacja_w_qt_8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWbOZlZvR5I/AAAAAAAAF3Y/N-RUbvWTOGA/meego-pierwsza_aplikacja_w_qt_8.png" width="500" /></a></div><br />
Czy miałem podczas wykonywania tych kroków jakieś błędy? Tak, był jeden, ale był on związany z tym, że próbowałem uruchomić aplikację w <i>Qt Creatorze</i> nie mając wcześniej wystartowanego środowiska testowego. Dostałem wówczas taki komunikat:<br />
<blockquote>Cleaning up remote leftovers first ...<br />
Error running initial cleanup: Nie można połączyć się z hostem.</blockquote>Jeśli będą pojawiać się inne komunikaty i inne błędy, polecam najpierw upewnić się, że <i>Qt Creator</i> poprawnie łączy się z środowiskiem uruchomieniowym <i>MeeGo</i>. Można to sprawdzić przechodząc odpowiednio do <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Narzędzia</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Opcje</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Projekty</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">MeeGo Device Configuration</span> i tam wybierając z listy wcześniej przygotowaną konfigurację możemy skorzystać z opcji <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Test</span>. Jeśli tutaj wszystko pójdzie OK, nie powinno być problemów z wystartowaniem aplikacji.<br />
<br />
Mamy już pierwszą aplikację. Co dalej? Czekam na sugestie czytelników o czym chcieli by poczytać :-).Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-63146868462735474732011-02-21T20:43:00.001+01:002011-03-04T23:52:41.427+01:00MeeGo SDK - Konfiguracja Qt CreatorInstalując <i>MeeGo SDK</i> (<a href="http://rpetryniak.blogspot.com/2011/02/meego-pierwsze-starcie.html">pisałem o tym więcej w poprzednim wpisie</a>) dostajemy pełne środowisko programistyczne, które zawiera nie tylko wymagane biblioteki i środowisko uruchomieniowe, ale również wygodne narzędzie do programowania: <i>Qt Creator</i>. Jest to w pełni funkcjonalne środowisko programistyczne dla <i>Qt</i>, które zawiera wiele wygodnych mechanizmów ułatwiających wykonywanie często żmudnych czynności: pełna obsługa kilku systemów śledzenia wersji, wspomaganie procesu kompilacji, narzędzia do testowania i debugowania, zintegrowaną dokumentację, kolorowanie składni i wiele innych. Narzędzie to zostało również przystosowane przez firmę <i>Nokia</i> do programowania pod platformę <i>MeeGo</i>. Wystarczy wskazać i skonfigurować dostęp do środowiska wdrożeniowego (<i>MeeGo Target</i>) oraz środowiska uruchomieniowego (<i>MeeGo Runtime</i>), aby łatwo zacząć programować aplikacje mobilne i tym się właśnie zajmę w dzisiejszym wpisie. Od razu na początku wspomnę, że wszystkie opisane w tym poradniku kroki są bardzo dobrze opisane na <i>Wiki projektu</i>, na którym się wzorowałem. W razie problemów polecam zajrzeć na strony:<br />
<ul><li><a href="http://wiki.meego.com/SDK/Docs/1.1/Getting_started_with_the_MeeGo_SDK_for_Linux#Configuring_Qt_Creator_to_use_the_MeeGo_toolchain.28s.29">Configuring Qt Creator to use the MeeGo toolchain(s)</a></li>
<li><a href="http://wiki.meego.com/SDK/Docs/1.1/Configuring_QEMU_runtimes#Configuring_access_to_an_emulated_device_in_Qt_Creator">Configuring access to an emulated device in Qt Creator</a></li>
</ul><span class="Apple-style-span" style="font-size: large;">Konfiguracja narzędzi kompilujących i bibliotek </span><span class="Apple-style-span" style="font-size: large;"><i>MeeGo</i></span><br />
<br />
Aby skonfigurować w <i>Qt Creator</i> dostęp o narzędzi kompilujących i bibliotek <i>MeeGo</i> (tzw. <i>MeeGo Toolchain</i>) wykonujemy następujące czynności:<br />
<ol><li>Uruchamiamy <i>Qt Creator</i>. Może to być ten dostarczony z <i>MeeGo</i>, lub ten z którego do tej pory korzystaliśmy w systemie (najlepiej oczywiście jakby była to najnowsza dostępna wersja, aby mieć pewność, że będzie wspierała <i>MeeGo</i>). Ja wybrałem to drugie podejście, aby nie rozdrabniać sobie środowiska programistycznego. Zresztą na stronie Wiki również tak proponują.</li>
<li>Wchodzimy do <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Narzędzia</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Opcje</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Qt4</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Wersje Qt</span>.</li>
<li>Po prawej stronie zakładki <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Wersje Qt</span> klikamy przycisk z plusikiem, aby dodać nową wersję.</li>
<li>W polu <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Nazwa wersji</span> wpisujemy własną nazwę określającą wersję <i>Qt</i>. Może to być np. <b>MeeGo NetBook IA32 1.1.2</b>.</li>
<li>Podajemy również ścieżkę do <i>qmake</i> zainstalowanego w katalogach <i>MeeGo</i>. W moim systemie 64-bitowym było to akurat <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/usr/lib/madde/linux-x86_64/targets/meego-netbook-ia32-1.1.2/bin/qmake</span>. Z dokumentacji wynika, że dla architektur 32-bitowych powinno to być podobnie do <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/usr/lib/madde/linux-i686/targets/meego-netbook-ia32-1.1.2/bin/qmake</span>, jednak nie mam co do tego pewności i polecam sprawdzić samodzielnie.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWKcLobBHhI/AAAAAAAAF0g/13zF0AZzN0E/meego-dev-qtcreator_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="216" src="https://lh6.googleusercontent.com/_AeFiJN9phoM/TWKcLobBHhI/AAAAAAAAF0g/13zF0AZzN0E/meego-dev-qtcreator_1.png" width="320" /></a></div></li>
<li>Klikamy <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Przebuduj</span>, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Zastosuj</span> i <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ok</span>.</li>
<li>Aby zobaczyć więcej informacji o własnie skonfigurowanej wersji <i>Qt</i> wystarczy jeszcze raz wejść do tego samego okienka i kliknąć oraz przytrzymać wskaźnik myszy na etykiecie <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ustawienia ręczne</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">MeeGo NetBook IA32 1.1.2</span>. Powininny się wyświetlić informacje podobne jak na rysunku poniżej.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TWKcL17jWII/AAAAAAAAF0k/ZhhoYQ8d9CA/meego-dev-qtcreator_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TWKcL17jWII/AAAAAAAAF0k/ZhhoYQ8d9CA/meego-dev-qtcreator_2.png" width="262" /></a></div></li>
</ol><span class="Apple-style-span" style="font-size: large;">Dostęp do środowiska uruchomieniowego <i>MeeGo</i></span><br />
<br />
Aby skonfigurować dostęp do wybranego środowiska uruchomieniowego <i>MeeGo</i> (tzw. <i>MeeGo Runtime</i>) z poziomu <i>Qt Creatora</i> wystarczy wykonać następujące czynności:<br />
<ol><li>Otwieramy <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Narzędzia</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Opcje</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Projekty</span> → <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">MeeGo Device Configuration</span></li>
<li>i klikamy tutaj przycisk <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Add</span></li>
<li>Warto ustalić od razu nazwę dla tej konfiguracji. Może to być np. <b>MeeGo NetBook Emulator</b>.</li>
<li>Uzupełniamy pozostałe pola (przykład na rysunku poniżej):</li>
<ul><li>Device type: <b>MeeGo emulator</b></li>
<li>Authentication type: <b>Password</b></li>
<li>Host name: <b>localhost</b> (wartość domyślna)</li>
<li>SSH port: <b>6666</b> (wartość domyślna)</li>
<li>Gdb serwer port: <b>13219</b> (wartość domyślna)</li>
<li>Connection timeout: <b>30s </b>(wartość domyślna)</li>
<li>Username: <b>root</b></li>
<li>Password: <b>meego</b></li>
</ul><li>Klikamy <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Zastosuj</span></li>
</ol><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/_AeFiJN9phoM/TWKcMGYI91I/AAAAAAAAF0o/hMXD4HRO4oE/meego-dev-qtcreator_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="216" src="https://lh3.googleusercontent.com/_AeFiJN9phoM/TWKcMGYI91I/AAAAAAAAF0o/hMXD4HRO4oE/meego-dev-qtcreator_3.png" width="320" /></a></div>Aby przetestować właśnie skonfigurowane ustawienia należy najpierw uruchomić wybrane środowisko - można to zrobić wpisując w konsoli:<br />
<pre class="prettyprint lang-cpp notranslate">sudo mad remote -r meego-netbook-ia32-qemu-1.1.2-runtime poweron</pre>a następnie klikając przycisk <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Test</span> w okienku <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">MeeGo Device Configuration</span>. Jeśli wszystko jest OK powinniśmy zobaczyć okienko takie jak na zrzucie poniżej:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWKcMG9tSdI/AAAAAAAAF0s/8Zd_w6Luupw/meego-dev-qtcreator_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://lh4.googleusercontent.com/_AeFiJN9phoM/TWKcMG9tSdI/AAAAAAAAF0s/8Zd_w6Luupw/meego-dev-qtcreator_4.png" width="206" /></a></div>Co z tego wynika i po co to wszystko opiszę w kolejnym wpisie z przykładem pierwszej aplikacji dla <i>MeeGo </i>:-)Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-59365901334045302932011-02-18T22:17:00.002+01:002011-03-04T23:52:41.429+01:00MeeGo - pierwsze starcieGłośno w ostatnim czasie zrobiło się o <i><b>MeeGo</b></i> i to nie za sprawą wysypu urządzeń z tą platformą mobilną, a wręcz przeciwnie - ostatnia decyzja kluczowego partnera projektu, <i>Nokii</i> o <a href="http://www.heise-online.pl/newsticker/news/item/Nokia-i-Microsoft-lacza-sily-1188203.html">wyborze Windows Phone 7</a> jako głównego systemu dla kolejnych generacji smartfonów postawiła duży znak zapytania co do przyszłości rozwoju <i>MeeGo</i>. Na szczęści drugi duży partner w rozwoju <i>MeeGo</i> - <i>Intel</i>, <a href="http://www.heise-online.pl/newsticker/news/item/Intel-wciaz-stawia-na-MeeGo-i-obiecuje-32-nanometrowy-procesor-Atom-1189676.html">zamierza ciągle intensywnie inwestować w tą platformę</a> i <a href="http://www.heise-online.pl/open/news/item/MeeGo-Intel-poszuka-innych-partnerow-1192918.html">zachęca do tego również inne firmy</a>. A tych <a href="http://www.linuxfoundation.org/node/6144"><span id="goog_1427157054"></span>firm już teraz jest sporo<span id="goog_1427157055"></span></a>. Do firm, które zadeklarował sie wspierać i używać <i>MeeGo</i> w swoich produktach mozna wyróżnić nie tylko takich gigantów branży elektronicznej jak <i>AMD</i>, <i>Asus</i>, <i>Acer</i>, <i>Cisco</i>, ale również wiele innych firm na czele z <i>BMW</i>.<br />
<br />
Ja od dawna kibicuję <i>MeeGo</i>, aby jak najszybciej weszło i zdobyło rynek urządzeń mobilnych i multimedialnych. Głównym powodem jest to, że bardzo wygodnie pracuje mi się z biblioteką <a href="http://rpetryniak.blogspot.com/2008/08/qt_20.html"><i>Qt</i></a>, a myśl, że program, który napiszę w <i>Linuksie</i> będzie działał nie tylko na <i>MS Window</i> i <i>Mac iOS,</i> jak to miało miejsce dotychczas, ale również na wszystkich urządzeniach obsługujących <i>MeeGo</i> wydaje się naprawdę ekscytująca. Ostatnie decyzje <i>Nokii</i> również mnie nie wprawiły w najlepsze samopoczucie, ale już zdążyłem ochłonąć :) i wiem, że najlepszą rzeczą, którą mogę w takim momencie zrobić to promować tą platformę i zachęcać do niej innych. W związku z tym chciałbym rozpocząć serię artykułów pokazujących jak rozpocząć przygodę z tym systemem. Od razu również dodam, że do tej pory nie miałem żadnego doświadczenia z <i>MeeGo</i> i moje wpisy raczej będą opisywać pierwsze kroki, które sam postawiłem, a nie zaawansowane zagadnienia z punktu widzenia eksperta w tej dziedzinie. <br />
<br />
Na początek przygotowałem mały poradnik jak zainstalować podstawowe środowisko uruchomieniowe i jak skonfigurować zestaw narzędzi, które będą niezbędne do implementacji własnych programów. Poradnik ten został przygotowany w oparciu o oficjalną dokumentację znajdująca się na <i>Wiki</i> projektu: <a href="http://wiki.meego.com/SDK/Docs/1.1/Getting_started_with_the_MeeGo_SDK_for_Linux">http://wiki.meego.com/SDK/Docs/1.1/</a><br />
<a href="http://wiki.meego.com/SDK/Docs/1.1/Getting_started_with_the_MeeGo_SDK_for_Linux">Getting_started_with_the_MeeGo_SDK_for_Linux</a>. Jest ona naprawdę dobrze przygotowana i mój opis będzie raczej jej skrótem, a nie rozwinięciem. Wszystkie instrukcje zostaną pokazane na przykladzie systemu <b><i>Ubuntu w wersji 10.10</i></b>.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Ustawienie repozytorium</span><br />
<br />
Wielkim ułatwieniem dla osób interesujących się testowaniem i rozwojem <i>MeeGo</i> w <i>Ubuntu</i> jest przygotowane dla tej dystrybucji repozytorium, które zawiera wszystkie niezbędne narzędzia do dalszej pracy. Konfigurujemy je w sposób opisany poniżej.<br />
<br />
Tworzymy nowy plik<br />
<pre class="prettyprint lang-bsh notranslate">sudo nano /etc/apt/sources.list.d/meego-sdk.list</pre>do którego zapisujemy:<br />
<pre class="prettyprint lang-bsh notranslate">deb http://repo.meego.com/MeeGo/sdk/host/repos/ubuntu/10.10/ /</pre><br />
Pobieramy klucze do repozytorium:<br />
<pre class="prettyprint lang-bsh notranslate">gpg --keyserver pool.sks-keyservers.net --recv 0BC7BEC479FC1F8A
gpg --export --armor 0BC7BEC479FC1F8A | sudo apt-key add -</pre><br />
Aktualizujemy listę pakietów:<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get update</pre><br />
Sprawdzamy poprawność repozytorium:<br />
<pre class="prettyprint lang-bsh notranslate">apt-cache policy madde</pre><br />
<span class="Apple-style-span" style="font-size: large;">Instalacja MeeGo SDK</span><br />
<br />
Aby zainstalować pakiet narzędzi <i>MeeGoo</i> (tzw. <i>SDK</i>) wystarczy wskazać do instalacji meta-pakiet <b><i>meego-sdk</i></b>. Dzięki temu wszystkie wymagane pakiety zostaną zainstalowane.<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install meego-sdk</pre><br />
<span class="Apple-style-span" style="font-size: large;">Instalacja środowiska wdrożeniowego (<i>Target</i>)</span><br />
<br />
Należy również zainstalować wybrane środowisko wdrożeniowe <i>MeeGo</i> (tzw. <i>MeeGoo Target</i>). W jego skład wchodzi zestaw aplikacji do kompilacji, linkowania i debugowania (tzw. <i>MeeGoo Toolchain</i>) oraz biblioteki systemowe.<br />
<br />
Aby sprawdzić jakie środowiska wdrożeniowe mamy do dyspozycji wydajemy polecenie:<br />
<pre class="prettyprint lang-bsh notranslate">sudo mad-admin list</pre><br />
Ja wybrałem do instalacji dwa z nich:<br />
<pre class="prettyprint lang-bsh notranslate">sudo mad-admin create -f meego-netbook-ia32-1.1.2
sudo mad-admin create -f meego-handset-ia32-1.1.2</pre><br />
Aby przetestować działanie wybranego środowiska, warto utworzyć dla niego przykładową aplikację. Można to szybko zrobić stosując narzędzia dostępne w <i>MeeGo SDK</i>:<br />
<pre class="prettyprint lang-bsh notranslate">mad -t meego-netbook-ia32-1.1.2 pscreate -t qt-simple qthello</pre><br />
Przechodzimy do katalogu z projektem i uruchamiamy kompilację:<br />
<pre class="prettyprint lang-bsh notranslate">cd qthello
mad -t meego-netbook-ia32-1.1.2 qmake
mad -t meego-netbook-ia32-1.1.2 make</pre><br />
Sprawdzamy czy plik wykonywalny istnieje:<br />
<pre class="prettyprint lang-bsh notranslate">file build/qthello</pre><br />
Aplikację również możemy uruchomić wydając polecenie: <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">build/qthello</span>. Powinno nam się pojawić okienko programu z widocznym przyciskiem „<i>Hello world</i>”. Okienko należy wyłączyć zamykając program w konsoli ponieważ nie zawiera ono paska tytułowego z przyciskami sterującymi (zamknij, minimalizuj, itp.).<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Instalacja i testowanie środowiska uruchomieniowego (<i>Runtime</i>)</span><br />
<br />
Środowisko wdrożeniowe to za mało, aby wygodnie pisać i testować programy napisane dla <i>MeeGo</i>. Potrzebne nam jest również środowisko uruchomieniowe (ang. <i>MeeGo Runtime</i>), na którym będziemy mogli przetestować na żywo jak się sprawuje nasza aplikacja. Najlepiej jakbyśmy dysponowali jakimś urządzeniem, na którym jest zainstalowany <i>MeeGo</i>. Jeśli jednak takim nie dysponujemy możemy skorzystać z testowego środowiska przygotowanego przez twórców <i>MeeGo</i> w postaci maszyn wirtualnych <i>QEMU</i>. Oczywiście do ich działania wymagana jest sprzętowa akceleracja wirtualizacji wbudowana w procesor. W sumie nie jest to wymóg samego <i>MeeGo</i>, a jedynie <i>QEMU</i>.<br />
<br />
Instalacja środowiska uruchomieniowego jest dobrze opisana na <i>Wiki MeeGo</i>, gdzie również polecam zajrzeć: <a href="http://wiki.meego.com/SDK/Docs/1.1/Configuring_QEMU_runtimes">http://wiki.meego.com/SDK/Docs/1.1/Configuring_QEMU_runtimes</a>.<br />
<br />
Aby sprawdzić jakie środowiska mamy do dyspozycji wydajemy polecenie:<br />
<pre class="prettyprint lang-bsh notranslate">mad-admin list runtimes</pre><br />
Ja zainstalowałem na początek dwa z nich (uwaga: każde zajmuje ok. 650MB!):<br />
<pre class="prettyprint lang-bsh notranslate">sudo mad-admin create -f -e meego-netbook-ia32-qemu-1.1.2-runtime
sudo mad-admin create -f -e meego-handset-ia32-qemu-1.1.2-runtime</pre><br />
Po instalacji możemy już przetestować na własnym komputerze czy to naprawdę działa:<br />
<pre class="prettyprint lang-bsh notranslate">sudo mad remote -r meego-netbook-ia32-qemu-1.1.2-runtime poweron
sudo mad remote -r meego-handset-ia32-qemu-1.1.2-runtime poweron</pre><br />
Po zakończonych testach wyłączamy wirtualną maszynę poleceniem:<br />
<pre class="prettyprint lang-bsh notranslate">sudo mad remote -r meego-netbook-ia32-qemu-1.1.2-runtime poweroff
sudo mad remote -r meego-handset-ia32-qemu-1.1.2-runtime poweroff</pre><br />
<span class="Apple-style-span" style="font-size: large;">Napotkane problemy</span><br />
<br />
<ul><li>Niestety przy podczas pierwszej próby włączenia dowolnego środowiska dostawałem komunikat:<br />
<pre class="prettyprint lang-bsh notranslate">Starting QEMU runtime meego-handset-ia32-w32-qemu-1.1.20101031.2201-sda-runtime
This may take some time ...</pre>i nic się więcej nie działa. Nic nie startuje. Nic nie zjada zasobów procesora. Wyszło na to, że mój procesor (<i>AMD Phenom™ II X4 925</i>) ma jakieś problemy ze wspomaganiem wirtualizacji sprzętowej. Jest też odpowiedni wpis na <i>Wiki Meego</i>, który dotyczy tego problemu i tam polecam najpierw zajrzeć: <a href="http://wiki.meego.com/SDK/Docs/1.1/MeeGo_SDK_Graphics_Acceleration">http://wiki.meego.com/SDK/Docs/1.1/MeeGo_SDK_Graphics_Acceleration</a>. U mnie problem rozwiązał się po instalacji następujących pakietów:<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install qemu-kvm kvm</pre>wydaniu polecenia:<br />
<pre class="prettyprint lang-bsh notranslate">sudo modprobe kvm_amd</pre>i restarcie komputera. Później już wszystko ruszyło z miejsca :)</li>
<li>Na innym komputerze, co do którego miałem pewność, że procesor, który się w nim znajduje (<i>Intel i3 530</i>) powinien sobie poradzić był taki sam problem ze uruchomieniem środowiska <i>Runtime</i>. Rozwiązanie było podobne jak poprzednio. Tutaj wystarczyło jedynie wpisać:<br />
<pre class="prettyprint lang-bsh notranslate">sudo modprobe kvm_intel</pre></li>
<li>Niestety na komputerze z <i>AMD</i> nie udało mi się uruchomić, żadnego środowiska uruchomieniowego dla telefonów. Domyślam się, że może to być związane z tym, że mam na nim kartę i sterowniki <i>NVidi</i>. Na komputerze z Intelem wszystko było OK.</li>
</ul><span class="Apple-style-span" style="font-size: large;">Podsumowanie i zrzuty ekranu</span><br />
<br />
Na koniec jeszcze dwa przykładowe zrzuty ekranu jak to wygląda na żywo.<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TV6yg5QXFXI/AAAAAAAAFzQ/T3YiUgSFbe8/MeeGoo_netbook_sdk1.1.2_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TV6yg5QXFXI/AAAAAAAAFzQ/T3YiUgSFbe8/MeeGoo_netbook_sdk1.1.2_1.png" width="500" /></a></div><div style="text-align: center;"><i>Środowisko dla netbooków</i></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://lh5.googleusercontent.com/_AeFiJN9phoM/TV6yYENGaCI/AAAAAAAAFy0/EbdjOPSeoVU/MeeGoo_handset_sdk1.1.2_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://lh5.googleusercontent.com/_AeFiJN9phoM/TV6yYENGaCI/AAAAAAAAFy0/EbdjOPSeoVU/MeeGoo_handset_sdk1.1.2_1.png" width="400" /></a></div><div style="text-align: center;"><i>Środowisko dla telefonów</i></div><br />
Więcej zrzutów można obejrzeć na poniższym pokazie slajdów:<br />
<div style="text-align: center;"><div style="font-family: arial,sans-serif; font-size: 13px; width: 600px;"><div><embed flashvars="host=picasaweb.google.com&noautoplay=1&hl=pl&feat=flashalbum&RGB=0x000000&feed=https%3A%2F%2Fpicasaweb.google.com%2Fdata%2Ffeed%2Fapi%2Fuser%2Frpetryniak%2Falbumid%2F5575089303272666065%3Falt%3Drss%26kind%3Dphoto%26hl%3Dpl" height="400" pluginspage="http://www.macromedia.com/go/getflashplayer" src="https://picasaweb.google.com/s/c/bin/slideshow.swf" type="application/x-shockwave-flash" width="600"></embed></div><span style="float: left;"><a href="https://picasaweb.google.com/rpetryniak/MeeGooSystem?feat=flashalbum" style="color: #3964c2;">Wyświetl wszystkie</a></span><br />
<div style="text-align: right;"><a href="http://picasaweb.google.com/lh/getEmbed?feat=flashalbum" style="color: #3964c2;">Uzyskaj własny</a></div></div></div><br />
Pomimo tego, że środowisko dla netbooków, wygląda tak jak wielu czytelników zapewne widziało na różnych screenach w Internecie to nie można powiedzieć już tego samego o wersji na telefony. Jest ona bardzo okrojona, zawiera jedynie kilka dodatkowych aplikacji i w ogóle nie prezentuje się najlepiej. Ale przecież, nie jest to środowisko do codziennego używania, a jedynie do testów i rozwoju własnych aplikacji.<br />
<br />
Na razie to tyle :) Kolejnym razem opiszę jak do tego wszystkiego podłączyć <i>Qt Creatora</i>.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0tag:blogger.com,1999:blog-19722740.post-36713427782304598382010-12-11T23:58:00.001+01:002010-12-12T00:04:20.724+01:00Kompilacja KDE ze źródeł dla niewtajemniczonychKilka dni temu postanowiłem zmierzyć się z wyzwaniem samodzielnej kompilacji <i>KDE</i> ze źródeł. To co po pierwszych próbach wydawało mi się niemal niemożliwe, ostatecznie okazało się nie tak skomplikowane jak sądziłem. Podstawowy błąd jaki robiłem na początku był taki, że kompilację uruchamiałem na starszym wydaniu <i>Kubuntu 10.04</i>. Jest to wersja o dłuższym okresie serwisowania (tzw. <i>LTS</i> - <i>Long Term Support</i>), więc byłem przekonany, że programiści <i>KDE</i> będą ją również wspierać w procesie kompilacji. Tak poza tym, to właśnie tą wersję miałem zainstalowaną na komputerze stacjonarnym i to był główny powód, dlaczego z niej skorzystałem. O problemach jakie wtedy napotkałem nie będę pisał, bo nie występowały one już w <b>Kubuntu 10.10</b>. Tutaj jedynym problemem było zainstalowanie wszystkich wymaganych zależności na potrzeby kompilacji.<br />
<br />
Poniższy opis/poradnik powstał przy znacznym wykorzystaniu następujących materiałów dostępnych w Internecie:<br />
<ul><li>KDE TechBase: <i><a href="http://techbase.kde.org/Build_KDE4.6_on_Kubuntu">Build KDE4.6 on Kubuntu</a></i> (opis kompilacji <i>KDE 4.6</i> w <i>Kubuntu 10.04</i>)</li>
<li>KDE TechBase: <i><a href="http://techbase.kde.org/Getting_Started/Build/KDE4">Getting Started/Build/KDE4</a></i> (oficjalna dokumentacja kompilacji <i>KDE</i>)</li>
<li>Blog: Who Says Penguins Can't Fly?: <i><a href="http://hanschen.org/2010/01/20/building-kde-sc-from-svn/">Building KDE SC from SVN</a></i>.</li>
</ul>Warto do nich zajrzeć w razie pojawienia się innych niż opisane w tym tekście problemów, lub jeśli będzie potrzeba bardziej dokładnego doczytania się co skąd się bierze, bo ja nie zawsze dokładnie to przedstawiam. <br />
<ul></ul><span style="font-size: large;">Aktualizacja oprogramowania</span><br />
<br />
<i>Kubuntu</i> w podstawowej konfiguracji zawiera jedynie wpisy do przetestowanych repozytoriów, co do których w miarę jest pewność, że pakiety tam zawarte będą działać stabilnie. Nie ma dodanych tych, które zawierają rozwojowe i testowe wersje pakietów. One właśnie są potrzebne w momencie kompilacji <i>KDE</i>. Dodajemy zatem te repozytoria i instalujemy aktualizacje:<br />
<pre class="prettyprint lang-bsh notranslate">sudo add-apt-repository ppa:kubuntu-ppa/backports
sudo add-apt-repository ppa:kubuntu-ppa/beta
sudo apt-get update
sudo apt-get upgrade</pre><br />
Nie wiem dlaczego, ale po wykonaniu tych kroków narzędzie do aktualizacji z <i>Kubuntu</i> poinformowało mnie (trzeba sprawdzić odpowiednią ikonkę w zasobniku systemowym koło zegara), że jeszcze nie wszystkie zostały zainstalowane. Instalujemy wszystkie aktualizacje, które zostały zaproponowane.<br />
<br />
<span style="font-size: large;">Instalujemy dodatkowe pakiety</span><br />
<br />
Tak jak wspomniałem na początku tego wpisu, na potrzeby kompilacji wymagane jest zainstalowanie wielu dodatkowych pakietów. Poniższa lista została opracowana na podstawie oficjalnej dokumentacji kompilacji <i>KDE</i> pod <i>Debiana</i> i <i>Ubuntu</i>/<i>Kubuntu</i>: <a href="http://techbase.kde.org/Getting_Started/Build/KDE4/Kubuntu_and_Debian">http://techbase.kde.org/Getting_Started/Build/KDE4/Kubuntu_and_Debian</a> oraz na podstawie własnych doświadczeń zebranych podczas kilku prób kompilacji <i>KDE</i>.<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install subversion git-core gcc g++
sudo apt-get install build-essential xorg-dev cdbs debhelper cmake kdesdk-scripts subversion ssh xserver-xephyr doxygen dbus-x11 libxml2-dev libxslt1-dev shared-mime-info libical-dev libgif-dev libssl-dev libboost-dev libboost-program-options-dev libboost-graph-dev libgpgme11-dev libxine-dev libqimageblitz-dev libbz2-dev libdbus-1-dev libpam0g-dev libpcre3-dev libkrb5-dev libsm-dev libclucene0ldbl libclucene-dev libjpeg62-dev libxtst-dev xsltproc libxrender-dev libfontconfig1-dev automoc librdf0-dev libdbusmenu-qt-dev docbook-xsl docbook-xml libattica-dev libqt4-webkit-dev shared-desktop-ontologies libphonon-dev
sudo apt-get install graphviz libqt4-dev libstreamanalyzer-dev libstrigiqtdbusclient-dev libxml2-utils libopenexr-dev libjasper-dev libenchant-dev libavahi-common-dev libaspell-dev libasound2-dev libldap2-dev libsasl2-dev libsmbclient-dev libxkbfile-dev libxcb1-dev libxklavier-dev libxdamage-dev libxcomposite-dev libbluetooth-dev libusb-dev network-manager-dev libsmbclient-dev libsensors-dev libnm-util-dev libusb-dev libcfitsio3-dev libnova-dev libeigen2-dev libopenbabel-dev libfacile-ocaml-dev libboost-python-dev libsvn-dev libsvncpp-dev libqt4-dev libqca2-dev libstreamanalyzer-dev libstrigiqtdbusclient-dev libcommoncpp2-dev libidn11 libidn11-dev libpci-dev libxss-dev libxft-dev libpoppler-qt4-dev libpolkit-agent-1-dev libpolkit-backend-1-dev libpolkit-qt-1-dev libspectre-dev
sudo apt-get install kdesdk kdelibs5-dev kdebase-workspace-dev libakonadi-dev libsoprano-dev libkonq5-dev libv4l-dev
sudo apt-get install flex bison libacl1-dev hspell</pre><br />
<span style="font-size: large;">Utworzenie nowego użytkownika</span><br />
<br />
Przed przystąpieniem do właściwego procesu kompilacji <i>KDE</i>, warto zadać sobie pytanie: w jaki sposób chcemy mieć dostęp zarówno do nowego środowiska pracy jak również do wersji stabilnej, której używaliśmy do tej pory? W Internecie można znaleźć wiele odpowiedzi na to pytanie, m.in.: skorzystanie z środowiska wirtualnego (np. <i>VirtualBox</i>) lub przełączanie głównej gałęzi struktury plików przy użyciu <i>chroot</i>. W tym poradniku zostanie opisane jeszcze inne podejście, które jest zalecane w oficjalnej dokumentacji <i>KDE</i>. Zostanie utworzony nowy użytkownik, na koncie którego zostanie przeprowadzona kompilacja i lokalna instalacja <i>KDE</i>. "Lokalna" w tym wypadku oznacza, że tylko ten użytkownik będzie mógł korzystać z tej wersji <i>KDE</i>.<br />
<br />
Tworzymy nowego użytkownika i dodajemy go do grup. Jeśli chcemy, aby użytkownik miał dostęp do trybu poleceń administratora (<i>sudo</i>) zamiast do grupy <i>users</i> dodajemy go do grupy <i>admin</i>.<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo useradd -m -G users,audio,video,cdrom,plugdev -s /bin/bash kde-devel
sudo passwd kde-devel</pre><br />
Logujemy się na konto właśnie utworzonego użytkownika:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">su - kde-devel</pre><br />
<span style="font-size: large;">Konfiguracja skryptów startowych</span><br />
<br />
Oficjalna dokumentacja zaleca, aby zaraz po zalogowaniu skonfigurować skrypty startowe w celu usprawnienia procesu kompilacji. Można to zrobić wykonując następujące czynności:<br />
<ul><li>Robimy kopię zapasową oryginalnego pliku <i>.bashrc</i>:<pre class="prettyprint lang-bsh notranslate">cp .bashrc .bashrc_backup</pre></li>
<li>Ze strony <a href="http://techbase.kde.org/Getting_Started/Increased_Productivity_in_KDE4_with_Scripts/.bashrc">http://techbase.kde.org/Getting_Started/Increased_Productivity_in_KDE4_with_Scripts/.bashrc</a> kopiujemy zawartość przykładowego pliku <i>.bashrc</i> i wklejamy go do pliku <i>.bashrc_KDE</i>:<pre class="prettyprint lang-bsh notranslate">nano .bashrc_KDE</pre></li>
<li>Dodajemy do oryginalnego pliku <i>.bashrc</i> ładowanie pliku <i>.bashrc_KDE</i>:<pre class="prettyprint lang-bsh notranslate">echo "source ~/.bashrc_KDE" >> ~/.bashrc</pre></li>
<li>Wczytujemy ponownie zawartość pliku <i>.bashrc</i>, aby jego zawartość była widoczna również dla bieżącej sesji:<pre class="prettyprint lang-bsh notranslate">source ~/.bashrc</pre></li>
</ul><span style="font-size: large;">Konfiguracja narzędzia ''kdesrc-build''</span><br />
<br />
Na potrzeby kompilacji <i>KDE</i> najłatwiej skorzystać ze skryptu <b>kdesrc-build</b>, który można pobrać ze strony: <a href="http://kdesvn-build.kde.org/">http://kdesvn-build.kde.org/</a>. Skrypt ten automatyzuje wiele zadań, takich jak pobieranie źródeł, przygotowanie i uruchamianie procesu kompilacji.<br />
<br />
Pobranie i konfigurację skryptu <i>kdesrc-build</i> wykonujemy za pomocą poniższych poleceń (na stronie projektu warto najpierw sprawdzić, czy nie ma nowszej wersji tego narzędzia):<br />
<br />
<pre class="prettyprint lang-bsh notranslate">mkdir -p ~/kdesrc/kdesrc-build
wget http://kdesvn-build.kde.org/releases/kdesrc-build-1.12.tar.bz2
tar xjvf kdesrc-build-1.12.tar.bz2 -C ~/kdesrc/kdesrc-build/
rm kdesrc-build-1.12.tar.bz2
cp ~/kdesrc/kdesrc-build/kdesrc-build-1.12/kdesrc-buildrc-sample ~/.kdesrc-buildrc</pre><br />
Uruchomienie procesu kompilacji:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">~/kdesrc/kdesrc-build/kdesrc-build-1.12/kdesrc-build</pre><br />
Domyślnie proces kompilacji jest uruchamiany w trybie zrównoleglonym (opcja <span style="font-family: "Courier New",Courier,monospace;">-j</span> programu <i>make</i>) w 2 procesach. Warto jednak pamiętać, że:<br />
<ul><li>jeśli mamy 1 jeden rdzeń obliczeniowy to nie przyniesie to żadnego efektu;</li>
<li>w przypadku większej liczby rdzeni warto z nich skorzystać i uruchomić proces kompilacji z odpowiednio ustawionym parametrem <i>make-options</i> skryptu <i>kdesrc-build</i>. Przykład dla 4 procesorów:</li>
</ul><pre class="prettyprint lang-bsh notranslate">~/kdesrc/kdesrc-build/kdesrc-build-1.12/kdesrc-build --make-options=-j4</pre><br />
<span style="font-size: large;">Problemy</span><br />
<br />
Podczas kompilacji pojawił mi się następujący komunikat o błędzie:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">Building phonon-git (3/15)
Waiting for source code update.
Source update complete for phonon-git: 0 files affected.
Compiling...
Build succeeded after 1 second.
Installing phonon-git.
Unable to install phonon-git!
Overall time for phonon-git was 1 second.</pre><br />
Warto podejrzeć co znajduje się w logach. U mnie odpowiedni plik znajdował się w następującej lokalizacji:<br />
<pre class="prettyprint lang-bsh notranslate">cat ~/kdesvn/log/2010-12-05-03/phonon-git/install.log</pre><br />
<pre class="prettyprint lang-bsh notranslate">CMake Error at cmake_install.cmake:40 (FILE):
file INSTALL cannot copy file
"/home/kde-devel/kdesvn/phonon-git/qt_phonon.pri" to
"/usr/share/qt4/mkspecs/modules/qt_phonon.pri".</pre><br />
Wynika z niego, że pomimo przyjętych założeń o lokalnej instalacji <i>KDE</i> na koncie użytkownika <i>kde-devel</i>, instalator <i>Photona</i> próbuje umieścić coś w głównej strukturze plików. Można to wyłączyć edytując następujący plik:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">nano kdesvn/phonon-git/CMakeLists.txt</pre><br />
i umieszczając w komentarzu tą linię (znajduje się ona na końcu pliku):<br />
<br />
<pre class="prettyprint lang-bsh notranslate">#install(FILES qt_phonon.pri DESTINATION ${QT_MKSPECS_DIR}/modules)</pre><br />
<u>Uwaga</u>: Oczywiście nie mam pewności, czy to jest najlepsze z możliwych rozwiązań i czy później nie wpłynie to na błędy podczas używania nowego <i>KDE</i>. Najważniejsze jest jednak to, że kompilacja idzie dalej :-)<br />
<br />
<span style="font-size: large;">Kompilujemy dodatkowe programy</span><br />
<br />
Skrypt <i>kdesrc-build</i> poza domyślnym zestawem modułów, które mogliśmy zobaczyć podczas jego działania, obsługuje jeszcze kilka, które domyślnie są wyłączone. Aby je włączyć do procesu kompilacji, należy wedytować poniższej wskazany plik i odkomentować potrzebne nam moduły:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">nano ~/.kdesrc-buildrc</pre><br />
Zauważyłem jednak, że pewne moduły, które są tutaj umieszczone nie działają. Przykładem jest zestaw narzędzi developerskich <i>KDE</i>: <b>kdevplatform</b>, <b>kdevelop</b>, <b>quanta</b>. Problem z ich kompilacją wynika stąd, że ich kod nie jest dłużej dostępny w repozytorium <i>subversion</i> i został przeniesiony do repozytorium <i>git</i>. Mi osobiście zależało na tym, aby mieć te moduły skompilowane, dlatego poniżej znajduje się opis, jak można to zrobić samodzielnie.<br />
<br />
Zakładamy katalog, w którym będziemy umieszczać dodatkowe programy <i>KDE</i>, które będziemy samodzielnie kompilować:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">mkdir kdesoftware
cd kdesoftware</pre><br />
Pobieramy źródła programów związanych z <i>KDevelopem</i>:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">git clone git://gitorious.org/kdevelop/kdevplatform.git
git clone git://gitorious.org/kdevelop/kdevelop.git
git clone git://gitorious.org/kdevelop/quanta.git</pre><br />
Tworzymy katalogi budowania:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">mkdir -p build/{kdevplatform,kdevelop,quanta}</pre><br />
Uruchamiamy procedurę kompilacji i instalacji:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">cd ~/kdesoftware/build/kdevplatform/
cmake -DCMAKE_INSTALL_PREFIX=~/kde/ ../../kdevplatform/
make && make install
cd ~/kdesoftware/build/kdevelop/
cmake -DCMAKE_INSTALL_PREFIX=~/kde/ ../../kdevelop/
make && make install
cd ~/kdesoftware/build/quanta/
cmake -DCMAKE_INSTALL_PREFIX=~/kde/ ../../quanta/
make && make install</pre><br />
<span style="font-size: large;">Testowanie nowego KDE</span><br />
<ul><li>Jeśli chcemy przetestować pojedynczy program z właśnie skompilowanego <i>KDE</i>, a zarazem nie chcemy uruchamiać całego środowiska, najlepiej skorzystać z narzędzia <i>sux</i>:<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install sux
sux - kde-devel
dolphin</pre></li>
<li>Aby przetestować działanie pełnego środowiska <i>KDE</i> wystarczy zalogować się na użytkownika <i>kde-devel</i> wybierając jako typ sesji na „<i>Bezpieczne logowanie</i>” i uruchomić program:<pre class="prettyprint lang-bsh notranslate">~/kde/bin/startkde</pre></li>
<li> Jeśli chcielibyśmy to zrobić „bardziej elegancko” najlepszym rozwiązaniem wydaje się przygotowanie nowego typu sesji, którą będziemy mogli wybrać podczas logowania. Należy w tym celu wykonać następujące operacje (jako użytkownik z prawami administracyjnymi):<br />
<br />
<pre class="prettyprint lang-bsh notranslate">cd /usr/share/kde4/apps/kdm/sessions/
sudo cp kde-plasma.desktop kde-plasma-trunk.desktop
sudo nano kde-plasma-trunk.desktop</pre><br />
Do pliku <i>kde-plasma-trunk.desktop</i> wklejamy poniższą zawartość:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">[Desktop Entry]
Encoding=UTF-8
Type=XSession
Exec=/home/kde-devel/kde/bin/startkde
TryExec=/home/kde-devel/kde/bin/startkde
Name=KDE Trunk
Comment=The desktop made by KDE</pre><br />
Teraz już podczas logowania zamiast tradycyjnej sesji KDE, wybieramy z listy sesję <i>KDE Trunk</i>.<br />
</li>
</ul><br />
<span style="font-size: large;">Podsumowanie</span><br />
Powyższy opis tak jak tytuł wskazuje, został przygotowany dla użytkowników początkujących lub średnio obeznanych z administracją i programowaniem w <i>Linuksie</i>. Domyślam się, że zaawansowani użytkownicy <i>KDE</i> znaleźli by tu kilka błędów, lub przedstawili bardziej zoptymalizowany proces kompilacji. Dla mnie najważniejszym jednak jest to, że kompilację udało mi się nie tylko rozpocząć, ale również zakończyć :-), co pozwala mi się czuć bardziej wtajemniczonym w obsługę tego systemu, aniżeli jeszcze kilkanaście dni temu.<br />
<br />
Zachęcam do zadawania pytań lub do wpisywania komentarzy, co można poprawić w powyższym poradniku. W razie problemów poszukiwanie odpowiedzi najlepiej zacząć od oficjalnej dokumentacji na stronie: <a href="http://techbase.kde.org/Getting_Started/Build/KDE4/Troubleshooting">http://techbase.kde.org/Getting_Started/Build/KDE4/Troubleshooting</a>. Zostały tam opisane najczęstsze problemy i ich rozwiązania.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com4tag:blogger.com,1999:blog-19722740.post-58109416356836739512010-11-18T01:14:00.003+01:002010-11-18T09:44:25.649+01:00Wygodna integracja Windows7 z Ubuntu za pomocą VirtualBoxaPisałem ostatnio o <a href="http://rpetryniak.blogspot.com/2010/11/konfiguracja-lokalnego-serwera-pakietow.html">konfiguracji lokalnego repozytorium pakietów</a> na potrzeby sal dydaktycznych, którymi się opiekuję na <i>Politechnice Krakowskiej</i>. Na salach tych, o których mowa nie będzie zainstalowany jedynie <i>Ubuntu</i>, ale również <i>Windows7</i>. Aby ułatwić sobie dalszą pracę w zarządzaniu tym systemem, postanowiłem zainstalować go w maszynie wirtualnej stosując <a href="http://www.virtualbox.org/"><i>VirtualBoxa</i></a>. Jest wiele innych narzędzi do wirtualizacji, jednakże po lekturze różnych porównań w Internecie przekonałem się właśnie do wyboru narzędzia firmowanego logo <i>Oracle</i>. Oczywiście poza moją wygodą w administracji obu systemami na raz, najważniejsza jest wygoda i prostota obsługi przez potencjalnych użytkowników, którymi są studenci różnych kierunków <i>Wydziału Mechanicznego</i>. Ilość osób, które przewija się przez te sale w 2 tygodniowym toku zajęć (tygodnie parzyste i nieparzyste) zdecydowanie przewyższa pół tysiąca osób. Fakt ten wymaga, aby na sali wszystko działało bez zarzutu.<br />
<br />
W tym poradniku chciałem przedstawić poszczególne kroki konfiguracji wirtualnej maszyny z <i>Windows7</i> na potrzeby zajęć dydaktycznych. Na ile to będzie możliwe poszczególne czynności będą wykonywane za pomocą poleceń w konsoli, a nie za pomocą narzędzi graficznych.Takie podejście pozwala na pisanie skryptów, które potrafią znacznie ułatwić pracę w przypadku kiedy te same czynności należy wykonać kilka/kilkanaście razy. <br />
<br />
Jedną z najbardziej przydatnych, a niestety mało znanych funkcji <i>VirtualBoxa</i>, która zostanie omówiona jest możliwość zmiany trybu pracy dysku i przełączenie go w stan resetu danych po każdym wyłączeniu maszyny wirtualnej.<br />
<br />
Mam nadzieje, że przedstawione porady okażą się również przydatne dla typowego użytkownika komputera, a nie tylko dla osoby przygotowującej sale na zajęcia lub szkolenia.<br />
<br />
<span style="font-size: large;">Czynności wstępne</span><br />
<br />
Na początek gorąco polecam zapoznać się z oficjalną dokumentacją narzędzia <i><b>VBoxManage</b></i>, które pozwala na wykonanie tych samych (a nawet więcej) czynności co ogólnie znany interfejs graficzny <i>VirtualBoxa</i> (<a href="http://www.virtualbox.org/manual/ch08.html">strona dokumentacji tego narzędzia</a>). Większość kroków w tym poradniku wykonanych jest za jego pomocą. Nie piszę jednak szczegółowych opisów dla poszczególnych poleceń, dlatego właśnie odsyłam do dokumentacji.<br />
<br />
Pierwszą czynnością administracyjną, którą polecam na początek jest przygotowanie dodatkowego użytkownika, którego możemy nazwać np. "<span style="font-family: "Courier New",Courier,monospace;">windows</span>". Docelowo dla tego użytkownika zostanie ustawione, aby zaraz po zalogowaniu do systemu startowała wirtualna maszyna z <i>Windows7</i>. Można oczywiście ten krok pominąć i skonfigurować sobie <i>VirtualBoxa</i> do pracy na obecnie używanym koncie użytkownika. <br />
<br />
Dodać nowego uzytkownika w Ubuntu możemy wybierając odpowiednio w menu: <i> </i><br />
<i>System → Administracja → Użytkownicy i grupy → Dodaj</i>.<br />
<br />
<span style="font-size: large;">Instalacja VirtualBox</span><br />
Instalację <i>VirtualBoxa</i> najlepiej przeprowadzać zgodnie z oficjalnym opisem zamieszczonym na stronie: <a href="http://www.virtualbox.org/wiki/Linux_Downloads">http://www.virtualbox.org/wiki/Linux_Downloads</a>. W <i>Ubuntu</i> sprowadza się to do dodania odpowiedniego repozytorium, pobrania i dodania kluczy uwierzytelniających oraz instalacji następujących pakietów:<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install virtualbox-3.2 dkms</pre><br />
<span style="font-size: large;">Utworzenie wzorcowej maszyny wirtualnej</span><br />
Przed stworzeniem pierwszej maszyny wirtualnej, która może być wzorcem do tworzenia kolejnych maszyn trzeba zalogować się na konto "<span style="font-family: "Courier New",Courier,monospace;">windows</span>" i tam uruchomić <i>VirtualBoxa</i>. Zaraz po zaakceptowaniu licencji można wyłączyć program. <br />
<br />
W tym momencie można jeszcze utworzyć katalog, który będzie współdzielony pomiędzy <i>Ubuntu</i>, a <i>Windowsem</i>:<br />
<pre class="prettyprint lang-bsh notranslate">mkdir /home/windows/share</pre><br />
Tworzenie maszyny wirtualnej można wykonać w prosty sposób w konsoli. Takie podejście zostanie tutaj omówione. Osobom, które nie chcą szczegółowo wgłębiać się w poszczególne parametry i czytać dokumentację <i>VBoxManage</i>, polecam jednak interfejs graficzny. Tam wygodny kreator poprowadzi nas za rękę i znaczną część pracy zrobi za nas w tle. Później dla przećwiczenia polecam przygotowanie odpowiednich poleceń konsoli, które zrobią dokładnie to samo, co wcześniej ustawiliśmy w narzędziu graficznym.<br />
<br />
Tworzenie wirtualnej maszyny dla <i>Windows7</i> może składać się z następujących kroków:<br />
<ol><li>Utworzenie i automatyczne zarejestrowanie wirtualnej maszyny dla 32-bitowego <i>Windows7</i>:<pre class="prettyprint lang-bsh notranslate">VBoxManage createvm --name Windows7_01 --ostype Windows7 --register</pre></li>
<li>Szczegółowa konfiguracja parametrów wirtualnej maszyny (szczegóły można doczytać w <a href="http://www.virtualbox.org/manual/ch08.html#vboxmanage-modifyvm">dokumentacji polecenia <i>modifyvm</i></a>):<pre class="prettyprint lang-bsh notranslate">VBoxManage modifyvm Windows7_01 --memory 1780 --vram 40 --accelerate3d on --accelerate2dvideo on --boot1 dvd --boot2 disk --boot3 none --cpus 2 --pae on --hwvirtex on --largepages on --ioapic on --clipboard bidirectional --usb on --usbehci on</pre></li>
<li>Udostępnianie lokalnego katalogu dla zwirtualizowanego systemu:<pre class="prettyprint lang-bsh notranslate">VBoxManage sharedfolder add Windows7_01 --name "share" --hostpath "/home/windows/share"</pre></li>
<li>Utworzenie kontrolerów <i>IDE</i> i <i>SATA</i> dla nośników danych:<pre class="prettyprint lang-bsh notranslate">VBoxManage storagectl Windows7_01 --name "Kontroler SATA" --add sata --hostiocache on --controller IntelAhci
VBoxManage storagectl Windows7_01 --name "Kontroler IDE" --add ide --hostiocache on --controller PIIX4</pre></li>
<li>Utworzenie i podpięcie dysku twardego (dysk rozszerzalny o maksymalnym rozmiarze 60GB):<pre class="prettyprint lang-bsh notranslate">VBoxManage createhd --filename /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi --size 60000
VBoxManage storageattach Windows7_01 --storagectl "Kontroler SATA" --port 0 --device 0 --type hdd --medium /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi</pre></li>
<li>Podłączenie napędu <i>CD/DVD</i> z włączoną funkcją zapisu (oczywiście ma to sens jeśli mamy nagrywarkę):<pre class="prettyprint lang-bsh notranslate">VBoxManage storageattach Windows7_01 --storagectl "Kontroler IDE" --port 1 --device 1 --type dvddrive --medium host:/dev/sr0 --passthrough on</pre></li>
</ol>Tak przygotowana maszyna powinna być już widoczna i gotowa do uruchomienia z poziomu okienkowego interfejsu <i>VirtualBoxa</i>.<br />
<br />
<span style="font-size: large;">Zarządzanie VirtuaBoxem z poziomu konsoli</span><br />
Do innych czynności, które możemy wykonać w konsoli można zaliczyć:<br />
<ul><li><u>Uruchamianie maszyny wirtualnej</u>. Maszynę możemy uruchamiać na dwa sposoby:<br />
<pre class="prettyprint lang-bsh notranslate">VBoxManage startvm Windows7_01
VirtualBox -startvm Windows7_01</pre>Generalnie skutek wykonania obu poleceń będzie ten sam. Jedyną różnicą jaką ja zauważyłem jest uruchomienie zadania w tle przez pierwsze polecenie, w odróżnieniu od drugiego polecenia, gdzie wiersz polecenia oczekuje na zamkniecie programu <i>VirtualBox</i>. To drugie polecenie przyda się właśnie na etapie konfiguracji <i>GDM</i> (opis w dalszej części poradnika).<br />
</li>
<li><u>Rejestrowanie maszyny wirtualnej</u>. Przydaje się szczególnie jeśli przekopiujemy maszynę na inny komputer i chcemy z niej korzystać.<br />
<pre class="prettyprint lang-bsh notranslate">VBoxManage registervm /home/windows/.VirtualBox/Machines/Windows7_01/Windows7_01.xml</pre></li>
<li><u>Wyrejestrowywanie</u>. Jeśli maszyna nie będzie dłużej wykorzystywana na bierzącym komputerze, bo np. została przeniesiona na inny, warto ją usunąć z wewnętrznego rejestru maszyn wirtualnych <i>VirtualBoxa</i>.<br />
<pre class="prettyprint lang-bsh notranslate">VBoxManage unregistervm Windows7_01</pre></li>
</ul><span style="font-size: large;">Tworzenie kopii maszyny wirtualnej</span><br />
Kiedy mamy już przygotowaną wzorcową maszynę wirtualną z <i>Windows7</i> i chcielibyśmy ją powielić na inne komputery, warto najpierw zmniejszyć rozmiar wirtualnego dysku. W tym celu należy wykonać dwie czynności:<br />
<ul><li>Zerowanie pustych bloków na dysku w <i>Windows7</i> (<u>uwaga</u>: dysk musi być w trybie <i>normal</i>, a nie <i>immutable</i> - tryby te opisane są w dalszej części poradnika). Pomocne w tej czynności będzie małe narzędzie <i><b>sdelete</b></i>, które można pobrać ze strony <i>Technet Microsoft</i> (<a href="http://technet.microsoft.com/en-us/sysinternals/bb897443.aspx">link do narzędzia</a>). W wierszu poleceń w systemie <i>Windows</i> wpisujemy:<br />
<pre class="prettyprint lang-bsh notranslate">sdelete -c</pre></li>
<li>Kompaktowanie dysku w <i>VirtualBox</i>. Stosujemy polecenie:<pre class="prettyprint lang-bsh notranslate">VBoxManage modifyhd /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi --compact</pre></li>
</ul>Zabiegi te w moim wypadku pozwoliły na odchudzenie dysku wirtualnego z 9.4GB na 8.3GB. Ten 1GB potrafi zrobić różnicę podczas klonowania wielu dysków wirtualnych i ich późniejszego kopiowania na inne komputery.<br />
<br />
Kolejnym krokiem jest tzw. proces <i>klonowania dysku</i>. Jest to nic innego jak jego kopiowanie z nadaniem mu unikalnego identyfikatora. <br />
<pre class="prettyprint lang-bsh notranslate">VBoxManage clonehd /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi /home/windows/.VirtualBox/HardDisks/Windows7_02.vdi --format VDI</pre><br />
Na sam koniec, kiedy przekopiujemy katalog z ustawieniami maszyny wirtualnej oraz dysk wirtualny na inny komputer warto się upewnić, że są ustawione odpowiednie prawa dostępu dla użytkownika <i>windows</i>:<br />
<pre class="prettyprint lang-bsh notranslate">sudo chown windows -R /home/windows/.VirtualBox
sudo chgrp windows -R /home/windows/.VirtualBox</pre><br />
<span style="font-size: large;">Blokada dysków do zapisu</span><br />
Dyski w <i>VirtualBoxie</i> mogą pracować w różnych trybach zapisu (po szczegóły odsyłam do <a href="http://www.virtualbox.org/manual/ch05.html#hdimagewrites">oficjalnej dokumentacji</a>). Co ciekawe przełaczać się między tymi trybami można jedynie za pomocą narzędzia <i>VBoxManage</i> - okienkowa wersja tego nie umożliwia. Mnie osobiście poza standardowym trybem <i>normal</i>, zainteresował szczególnie tryb <i>immutable</i>, który nie zezwala na zapisywanie trwałych zmian na wirtualnym dysku. Dzięki temu po każdych zajęciach ze studentami, system <i>Windows</i> jest przywracany do pierwotnego, wzorcowego stanu. <br />
<br />
Technicznie działa to w ten sposób, że wszystkie zmiany jakie wprowadzi użytkownik podczas pracy w systemie zapisywane są na tak zwanym dysku różnicowym. Stan tego dysku jest resetowany po każdym wyłączeniu maszyny. W przypadku restartu maszyny stan nie jest kasowany, co ma zabezpieczać naszą tymczasową pracę na wypadek zawieszenia się systemu goszczącego (w tym wypadku <i>Windows7</i>). <br />
<br />
Do przełączania między tymi dwoma stanami najwygodniej będzie przygotować osobne skrypty:<br />
<ul><li>Przejście z trybu <i><b>normal</b></i> do <i><b>immutable</b></i>:<pre class="prettyprint lang-bsh notranslate">#!/bin/bash
VBoxManage storageattach Windows7_01 --storagectl "Kontroler SATA" --port 0 --device 0 --type hdd --medium none
VBoxManage modifyhd /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi settype immutable
VBoxManage storageattach Windows7_01 --storagectl "Kontroler SATA" --port 0 --device 0 --type hdd --medium /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi
echo `date`" : convert normal to immutable" >> vm_log.txt</pre></li>
<li>Przejście z trybu <i><b>immutable</b></i> do <i><b>normal</b></i>:<pre class="prettyprint lang-bsh notranslate">#!/bin/bash
VBoxManage storageattach Windows7_01 --storagectl "Kontroler SATA" --port 0 --device 0 --type hdd --medium none
VBoxManage modifyhd /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi settype normal
for file in `ls /home/windows/.VirtualBox/Machines/Windows7_01/Snapshots/*.vdi`
do
VBoxManage closemedium disk $file --delete
done
VBoxManage storageattach Windows7_01 --storagectl "Kontroler SATA" --port 0 --device 0 --type hdd --medium /home/windows/.VirtualBox/HardDisks/Windows7_01.vdi
echo `date`" : convert immutable to normal" >> vm_log.txt</pre></li>
</ul>W obu skryptach pierwszą i ostatnia czynnością jest odpowiednio odłączenie i podłączenie dysku do wirtualnego kontrolera <i>SATA</i>. W drugim skrypcie mamy dodatkowo odłączenie i skasowanie dysków różnicowych. Czynność ta zapewnia poprawną pracę w trybie <i>normal</i> i umożliwia wprowadzania dalszych zmian na wzorcowych dyskach wirtualnych (np. doinstalowanie kolejnych programów niezbędnych do zajęć).<br />
<br />
<span style="font-size: large;">Konfiguracja GDM</span><br />
Do tej pory opisywane kroki należały raczej do standardowych czynności administracyjnych dotyczących zarządzania wirtualna maszyną <i>VirtualBox</i>. Jakiejś szczególnej <u>wygody</u>, o której jest mowa w tytule nie widać. Trzeba się zwyczajnie zalogować do systemu, uruchomić <i>VirtualBoxa</i> i wystartować maszynę z <i>Windows7</i>. Może nie jest to jakość szczególnie skomplikowane, ale każdorazowo wymaga wykonania kilku tych samych czynności. Aby to zmienić i nieco usprawnić uruchamianie <i>Windowsa</i> można przygotować specjalną sesja dla menadżera logowania <i>GDM</i>. Dzięki temu już podczas logowania użytkownik będzie mógł wybrać sesję z <i>VirtualBoxem</i> i automatycznym startem maszyny wirtualnej.<br />
<br />
Na początek tworzymy i edytujemy skrypt <span style="font-family: "Courier New",Courier,monospace;">vm_start.bsh</span> do uruchamiania maszyny wirtualnej:<br />
<pre class="prettyprint lang-bsh notranslate">#!/bin/bash
VirtualBox -startvm Windows7_01</pre><br />
Później tworzymy nowy plik z ustawieniami dla sesji <i>GDM</i>:<br />
<pre class="prettyprint lang-bsh notranslate">sudo touch /usr/share/xsessions/windows7.desktop
sudo nano /usr/share/xsessions/windows7.desktop</pre>do którego wpisujemy takie ustawienia:<br />
<pre class="prettyprint lang-bsh notranslate">[Desktop Entry]
Encoding=UTF-8
Name=Windows7
Comment=Sesja skonfigurowana dla użytkownika "windows". Dla pozostałych nie działa.
Exec=/home/windows/.VirtualBox/vm_start.bsh
TryExec=/home/windows/.VirtualBox/vm_tools/vm_start.bsh
Icon=
Type=Application</pre>Teraz wystarczy się już wylogować i zalogować ponownie wybierając z listy sesję "Windows7". Powininna wystartować sama maszyna wirtualna bez startu sesji środowiska <i>Gnome</i>. Po wyłączeniu <i>Windows</i> w maszynie wirtualnej zamknie nam się również <i>VirtualBox</i> i zostaniemy automatycznie wylogowani z systemu.<br />
<br />
<span style="font-size: large;">Logowanie bez podawania hasła</span><br />
Aby jeszcze bardziej ułatwić dostęp do <i>Windowsa</i> można wyłączyć konieczność podawania hasła przez użytkownika "<span style="font-family: "Courier New",Courier,monospace;">windows</span>". W tym celu logujemy się na konto z uprawnieniami roota i zmieniamy ustawienia użytkownika "<span style="font-family: "Courier New",Courier,monospace;">windows</span>" ustawiając opcję "<i>Bez pytania o hasło przy logowaniu</i>". Dzięki temu przy kolejnym logowaniu nie będzie trzeba podawać hasła i od razu wystartuje maszyna wirtualna. Warto również zwrócić uwagę, że dla użytkownika "<span style="font-family: "Courier New",Courier,monospace;">windows</span>" nie będzie już możliwości zmiany sesji na inną (nie pojawi się okienko wyboru sesji, tylko użytkownik od razu zostanie zalogowany). Jeśli zależało by nam jednak na tym, aby znowu z poziomu użytkownika "<span style="font-family: "Courier New",Courier,monospace;">windows</span>" dostać do Gnome, w celu zmiany jakichś ustawień, to na powrót ustawiamy konieczość podawania hasła przez tego użytkownika. Można to również zrobić z linii komend poleceniem:<br />
<ul><li>Logowanie bez podawania hasła:<pre class="prettyprint lang-bsh notranslate">sudo usermod -a -G nopasswdlogin windows</pre></li>
<li>Konieczność podawania hasła:<pre class="prettyprint lang-bsh notranslate">sudo gpasswd -d windows nopasswdlogin </pre></li>
</ul><br />
<span style="font-size: large;">Problemy i rozwiązania</span><br />
Jedynym problemem jaki do tej pory spotkałem w korzystaniu z <i>VirtualBoxa</i>, było okresowe blokowanie się klawiatury w maszynie goszczącej. Zauważyłem, że dzieje się to zawsze po dłuższym czasie braku aktywności na komputerze, kiedy włączy się wygaszać ekranu w <i>Ubuntu</i>. Rozwiązanie oczywiście jest banalne - wystarczy wyłączyć wygaszać dla użytkownika "<span style="font-family: "Courier New",Courier,monospace;">windows</span>". W menu otwieramy kolejno: <i>System → Preferencje → Wygaszacz ekranu</i> i dla pewności ustawiamy:<br />
<ul><li>Przejęcie w stan bezczynności po: <u>2 godzinach</u></li>
<li>Uruchomienie wygaszacza ekranu gdy komputer jest w stanie bezczynności: <u>wyłączyć</u></li>
<li>Blokowanie ekranu, gdy wygaszacz jest aktywny: <u>wyłączyć</u></li>
</ul><br />
<span style="font-size: large;"> </span><br />
<span style="font-size: large;">Podsumowanie</span><br />
Przedstawiony poradnik oczywiście nie wyczerpuje tematu konfiguracji <i>VirtualBoxa</i> w Linuksie. Nie jest to prawdopodobnie również jedyne rozwiązanie postawionych problemów, a pewne rzeczy może dało by się zrobić lepiej. Co sądzicie? Czekam na komentarze i spostrzeżenia z używania tego poradnika. Tylko proszę nie pisać do mnie <i>per Pan</i>, bo przecież nie jesteśmy na zajęciach, tylko na blogu :)Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com6tag:blogger.com,1999:blog-19722740.post-57034466189671945492010-11-12T08:55:00.000+01:002010-11-12T08:55:48.218+01:00Konfiguracja lokalnego serwera pakietów Apt-Cacher NGW ostatnim czasie stanąłem naprzeciwko wyzwania szybkiej wymiany komputerów i oprogramowania na salach dydaktycznych, którymi się opiekuję na Politechnice Krakowskiej. Całościowy poradnik z przebiegu tej pracy przygotuję niebawem, jednakże już dzisiaj chciałem się podzielić doświadczeniami z konfiguracji lokalnego serwera pakietów <b><i>Apt-Cacher NG</i></b>.<br />
<br />
Jednym z systemów, który miał zostać zainstalowany na każdym z 20 nowych komputerów miał być <i>Ubuntu 10.04.1 LTS</i> w wersji 64 bitowej. Najpierw postanowiłem przeprowadzić próbną instalację na jednej z maszyn, a dopiero później na pozostałych. Nie wiem z jakich powodów, ale po instalacji systemu, instalacja dodatkowych pakietów, a raczej ich pobieranie z Internetu trwało ok. 3 godziny! Na to nie mogłem sobie pozwolić podczas właściwej instalacji, ponieważ na montaż sprzętu i instalację systemu przed pierwszymi zajęciami miałem niewiele więcej czasu. Dodam jeszcze, że instalator zakomunikował mi pobranie z Internetu ok. <i>350MB</i> danych do aktualizacji. <br />
<br />
Sytuację udało się opanować dzięki skonfigurowaniu jednego komputera jako lokalnego systemu pakietów. Jego rolą miało być <b>buforowanie</b> (w slangu polsko-angielskim można by powiedzieć <i>keszowanie</i> ;-) ) pakietów pobranych z Internetu i udostępnienie ich innym maszynom w sieci lokalnej. W tym momencie w <i>Ubuntu</i> najłatwiej to osiągnąć stosując narzędzie <i>Apt-Cacher NG</i> (<a href="http://www.unix-ag.uni-kl.de/%7Ebloch/acng/">strona projektu</a>). Poniższy poradnik udało mi się przygotować na podstawie kilku materiałów z Internetu, do których odsyłam w razie problemów:<br />
<ul><li><a href="http://ubuntuforums.org/showthread.php?t=981085">UbuntuForums.org: <i>How To Install Apt-Cacher-NG in Ubuntu Linux</i></a></li>
<li><a href="http://www.ubuntugeek.com/apt-cacher-ng-http-download-proxy-for-software-packages.html">UbuntuGeek.com: <i>Apt-Cacher-NG - HTTP download proxy for software packages</i></a></li>
<li><a href="http://jakilinux.org/linux/ubuntu/apt-cacher-w-ubuntu/">JakiLinux.org: <i>Apt-cacher w Ubuntu</i></a></li>
</ul><br />
<span style="font-size: large;">Konfiguracja lokalnego serwera pakietów</span><br />
<br />
Uaktualniamy lokalną bazę repozytoriów i pobieramy najnowsze aktualizacje:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get update
sudo apt-get upgrade</pre><br />
Instalacja narzędzia <i>apt-cacher-ng</i> z repozytorium:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get install apt-cacher-ng</pre><br />
<span style="font-size: large;">Konfiguracja serwera i klientów</span><br />
<br />
<u>Uwaga:</u> <i>Należy pamiętać o właściwym ustawieniu adresu IP lokalnego serwera pakietów. Ja używam IP: </i><i>192.168.120.102. Należy go oczywiście zmienić na własny.</i><br />
<ul><li>Konfigurujemy system pakietów <i>apt</i> (aplikacje <i>apt-get</i>, <i>aptitude</i> i inne), aby używał <i>apt-cacher-ng</i>:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo echo 'Acquire::http { Proxy "http://192.168.120.102:3142"; };' | sudo tee /etc/apt/apt.conf.d/01apt-cacher-ng-proxy</pre><br />
Możemy to oczywiście wyłączyć poleceniem:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo rm /etc/apt/apt.conf.d/01apt-cacher-ng-proxy</pre><br />
</li>
<li>Konfiguracja Synaptic z konsoli:<br />
<br />
<u>Uwaga:</u> <i>Jeśli pracujemy na nowym komputerze zaraz po instalacji, należy najpierw uruchomić na nim Synaptica i odświeżyć listę pakietów. Można to zrobic również przez ssh łącząc się z opcją -X</i>.<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo grep --color -i 'proxy' /root/.synaptic/synaptic.conf || sudo nano /root/.synaptic/synaptic.conf</pre><br />
zamieniamy ostatnią linijkę, czyli znaki: ”<b>};</b>” na:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">useProxy "1";
httpProxy "192.168.120.102";
httpProxyPort "3142";
ftpProxy "192.168.120.102";
ftpProxyPort "3142";
noProxy "";
};</pre><br />
Sprawdzamy jeszcze raz, czy wprowadzone zmiany znajdują się w pliku:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo grep --color -i 'proxy' /root/.synaptic/synaptic.conf</pre><br />
</li>
<li>Wczytujemy ponownie listę pakietów:<br />
<br />
<pre class="prettyprint lang-bsh notranslate">sudo apt-get update</pre></li>
</ul><br />
<span style="font-size: large;">Import lokalnych plików instalacyjnych</span><br />
<br />
<u>Uwaga</u>: <b><i>Poniższe czynności wykonujemy tylko na serwerze!</i></b><br />
<br />
W tym kroku zostaną zaimportowane lokalne pliki <span style="font-family: "Courier New",Courier,monospace;">*.deb</span> do repozytorium <i>apt-cacher-ng</i>. Dzięki temu narzędzie <i>apt-cacher-ng</i> nie będzie pobierać ponownie już raz pobranych plików.<br />
<br />
<pre class="prettyprint lang-bsh notranslate">test -x /var/cache/apt-cacher-ng/_import || sudo mkdir -p -m 2755 /var/cache/apt-cacher-ng/_import
sudo mv -uf /var/cache/apt/archives/*.deb /var/cache/apt-cacher-ng/_import/</pre><br />
Rozpoczynamy importowanie uruchamiając skrypt znajdujący się w dostarczonym panelu kontrolnym: <a href="http://localhost:3142/acng-report.html?doImport=Start+Import#bottom">http://localhost:3142/acng-report.html?doImport=Start+Import#bottom</a><br />
<br />
<span style="font-size: large;">Panel kontrolny i dostęp do dokumentacji</span><br />
<br />
Twórcy narzędzia <i>Apt-Cacher NG</i> przygotowali bardzo prosty panel kontrolny (<i>Dashboard</i>), w którym użytkownik może zobaczyć statystyki z jego działania. Jest on dostępny pod adresem: <a href="http://localhost:3142/acng-report.html">http://localhost:3142/acng-report.html</a>.<br />
<br />
Warto również zajrzeć do oficjalnej dokumentacji, która po instalacji pakietu będzie znajdować się w poniższej lokalizacji (można kliknąć link i otworzyć w przeglądarce): <a href="file:///usr/share/doc/apt-cacher-ng/html/index.html">file:///usr/share/doc/apt-cacher-ng/html/index.html</a>.<br />
<br />
<span style="font-size: large;">Własne spostrzeżenia</span><br />
<br />
Już teraz, po pierwszych próbach muszę przyznać, że narzędzie to oszczędziło mi sporo czasu i oczywiście sporo nerwów (oczekiwanie na zakończenie aktualizacji, lub też na instalację dodatkowych programów niezbędnych do zajęć, potrafi być stresujące, jeśli te zajęcia rozpoczynają się za kilka minut). Dla przykładu pobranie aktualizacji na 20 maszynach z <i>Ubuntu</i>, nie trwało tak jak wcześniej wspominałem 3 godziny, a zaledwie 2 minuty. Taką różnicę naprawdę się czuje :-) <br />
<br />
Poza aktualizacjami zainstalowałem też kilkanaście dodatkowych pakietów. Nie wiem ile to było MB dla jednego komputera, ale wiem ile to było w sumie dla wszystkich. Poniżej załączam zrzut ekranu z Panelu kontrolnego, prezentujące statystyki pobrań pakietów z lokalnego serwera pakietów:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/linux/ubuntu-apt_cacher_ng.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://riad.pk.edu.pl/%7Erpetryniak/website_resources/images/linux/ubuntu-apt_cacher_ng.png" /></a></div><br />
<br />
Jak widać ok. 16 GB! danych zostało pobranych z lokalnego serwera pakietów. Jestem przekonany, że fakt ten tak samo cieszy (a może bardziej) administratorów naszej uczelnianej sieci i oczywiście administratorów głównych serwerów pakietów dla <i>Ubuntu</i>.<br />
<br />
Pozostałe uwagi i spostrzeżenia przedstawiam już w skróconej formie:<br />
<ul><li>Idea działania <i>Apt-Cacher NG</i> jest taka, że po instalacji danego pakietu z dowolnego komputera w sieci lokalnej, pakiet ten powinien być już dostępny dla innych maszyn na serwerze. Ja jednak dla pewności przyjąłem taką strategię, że dany pakiet najpierw instaluje na serwerze, a później na wszystkich klientach na raz.</li>
<li>Wydawać by się mogło, że importowanie pakietów trzeba wykonać tylko raz. Jednak zdarzyło mi się już kilka razy, że niektóre pakiety pomimo pierwszej instalacji z głównego serwera, znowu były z niego pobierane jeśli instalowałem go ponownie na innej maszynie. Po zauważeniu takiego faktu, warto ponownie zaimportować pakiety.</li>
<li>Wyliczanie statystyk transferu przez panel kontrolny potrafi zawiesić <i>Apt-Cacher NG</i>. Ostatnio 2-krotnie po uruchomieniu ich zliczania <i>Apt-Cacher NG</i> przestawał odpowiadać na zapytania maszyn klienckich. Możliwe, że po prostu nie radził sobie z analizą logów i gdzieś się zawieszał. </li>
</ul>Moim zdaniem <i>Apt-Cacher NG</i> jest narzędziem jak najbardziej godnym polecenia. <br />
<ul></ul>Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com1tag:blogger.com,1999:blog-19722740.post-21592764401988647482010-09-12T14:40:00.002+02:002010-09-12T15:18:38.670+02:00Nowe witryny po wakacjachNa moim <i>blogu</i> w czasie wakacji (jak również wcześniej) niewiele się działo. Nie oznacza to wcale, że moja aktywność w sieci została zupełnie zawieszona. Korzystając z odrobiny wolnego czasu, którą udało mi się znaleźć pomiędzy innymi obowiązkami, uporządkowałem uprzednio prowadzone <i>Wiki</i> (<span style="font-size: x-small;">nie podaje linku, ponieważ zostanie ono niedługo wyłączone</span>) i przygotowałem dwie nowe witryny:<br />
<br />
<table class="post-body entry-content"><tbody>
<tr align="center"><td colspan="2"><b>Moje zainteresowania naukowe i realizowane projekty </b>[<a href="http://sites.google.com/site/rafalpetryniakresearch/">link</a>]<b><br />
</b></td></tr>
<tr><td><a href="http://sites.google.com/site/rafalpetryniakresearch/" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://riad.usk.pk.edu.pl/%7Erpetryniak/website_resources/images/mywebsite-research.png" /></a></td><td valign="top">Strona zawiera opis moich zainteresowań naukowych oraz zestawienie programów, które udostępniłem jako <i>Open Source</i>.<br />
Aby dotrzeć do szerszego grona odbiorców jest ona prowadzona w języku angielskim.</td></tr>
<tr align="center"><td colspan="2"><b>Materiały dydaktyczne, kursy, poradniki </b>[<a href="http://sites.google.com/site/rafalpetryniakcourses/">link</a>]<b><br />
</b></td></tr>
<tr><td><a href="http://sites.google.com/site/rafalpetryniakcourses/" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://riad.usk.pk.edu.pl/%7Erpetryniak/website_resources/images/mywebsite-courses.png" /></a></td><td valign="top">Tutaj będą umieszczane wszystkie materiały dydaktyczne, które przygotowuję na potrzeby prowadzonych przeze mnie zajęć.<br />
Dodatkowo, planuję umieścić na niej inne opracowania (np. poradniki techniczne), które do tej pory pisałem do własnych celów.</td></tr>
</tbody></table>Obie witryny pomimo tego, że mają opracowaną strukturę treści, to zawierają tylko część materiałów, które docelowo planuję tam umieścić. Obie również prowadzone są w oparciu o <i>mechanizm Wiki</i>, dlatego treść nie zawsze będzie kompletna i dopracowana (teksty redaguję bezpośrednio na tych stronach, a nie wklejam gotowe z zewnętrznych edytorów).<br />
<br />
Mam nadzieje, że będą one ciekawym uzupełnieniem dla obecnych (i przyszłych) czytelników bloga.Rafał Petryniakhttp://www.blogger.com/profile/13338645699618423437noreply@blogger.com0