{"id":1788,"date":"2011-05-31T00:20:12","date_gmt":"2011-05-30T22:20:12","guid":{"rendered":"http:\/\/yeti.albascout.ro\/blog\/?p=1788"},"modified":"2011-05-31T00:33:32","modified_gmt":"2011-05-30T22:33:32","slug":"color-tracking-cu-opencv-si-python","status":"publish","type":"post","link":"https:\/\/yeti.albascout.ro\/blog\/color-tracking-cu-opencv-si-python\/","title":{"rendered":"Color tracking cu OpenCV \u0219i python"},"content":{"rendered":"<p>Ziceam c\u0103 voi reveni cu un mic post despre cum se poate face quick&#8217;n&#8217;dirty color tracking cu OpenCV \u0219i python. De ce <a href=\"http:\/\/opencv.willowgarage.com\/wiki\/\">OpenCV<\/a>? Pentru c\u0103 are tot ce-i trebuie. De ce python? Pentru c\u0103 pentru explica\u021bii \u0219i pentru fast prototyping nu cred ca este alternativ\u0103 mai bun\u0103 (e u\u0219or de citit, u\u0219or de scris, chiar dac\u0103 aplica\u021bia final\u0103 va trebui rescris\u0103 probabil \u00een C).<\/p>\n<p>Pe scurt, sarcina pe care o aveam de rezolvat a fost identificarea unei anumite zone de culoare, a formei \u0219i a dimensiunii ei, posibil \u0219i loca\u021bia ei \u00een imagine, ca s\u0103 putem s\u0103 ne ferim de adversar \u0219i s\u0103 nu \u00eel lovim.<\/p>\n<p>Post-ul \u0103sta exist\u0103 nu doar pentru amintirea mea a unor chestii descoperite pe diferite site-uri de al\u021bii ca mine \/ sau de s\u0103pat prin documenta\u021bie, ci \u0219i pentru c\u0103 mi s-a p\u0103rut foarte bizar s\u0103 nu g\u0103se\u0219ti dec\u00e2t fr\u00e2nturi de explica\u021bii. \u0218i cred c\u0103 poate fi interesant \u0219i pentru non-tehnici, pentru c\u0103 python e foarte u\u0219or de urm\u0103rit :)<!--more-->\u00cen primul r\u00e2nd, OpenCV ofer\u0103 structuri foarte fericite pentru \u00eenc\u0103rcarea imaginilor de pe disc, care se potrivesc apoi foarte bine cu preluarea de frame-uri de la o camer\u0103 (practic, sunt doar c\u00e2teva linii de cod de schimbat pentru a trece de la un static frame la preluarea de imagini de la camer\u0103).<\/p>\n<p>Spre exemplu, ca s\u0103 iei un frame de la o camer\u0103, nu e mai mult de f\u0103cut dec\u00e2t:<\/p>\n<pre lang=\"python\">capture = cv.CaptureFromCAM(1)\r\nimg = cv.QueryFrame(capture)\r\n\r\n# sau, pentru o imagine de pe hard\r\n\r\nimg = cv.LoadImage(\"\/home\/yeti\/Desktop\/roz5.jpg\", CV_LOAD_IMAGE_COLOR)<\/pre>\n<p>Pornim de la o imagine de 640 x 480, luat\u0103 cu o camer\u0103 ieftin\u0103 \u0219i nec\u0103jit\u0103<\/p>\n<figure id=\"attachment_1824\" aria-describedby=\"caption-attachment-1824\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz5.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1824\" title=\"roz5\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz5-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz5-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz5.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1824\" class=\"wp-caption-text\">original<\/figcaption><\/figure>\n<p>Acum, pentru a g\u0103si culoarea pe care o c\u0103utam \u00een imaginea cu care lucr\u0103m, putem folosi mai multe strategii, \u00een func\u021bie de scenariu, dar de multe ori e mai simplu s\u0103 trecem de la RGB (sau BGR, cum e reprezentarea \u00een OpenCV) la sistemul de culori HSV (Hue, Saturation, Value), de unde vom folosi Hue (de ce, <a href=\"http:\/\/en.wikipedia.org\/wiki\/HSL_and_HSV\">aici<\/a>)<\/p>\n<pre lang=\"python\"># facem o imagine noua, unde va fi stocata reprezentarea HSV\r\nhsv_img = cv.CreateImage(cv.GetSize(img), IPL_DEPTH_8U, 3)\r\n# ne trebuie o imagine cu un singur canal unde sa extragem componenta Hue\r\ntmp_img = cv.CreateImage(cv.GetSize(img), IPL_DEPTH_8U, 1)\r\n# transfomarea BGR -&gt; HSV\r\ncv.CvtColor(img, hsv_img, cv.CV_BGR2HSV)\r\n# extragerea componentelor H, S, V din imaginea initiala (se procedeaza la fel si pentru BGR)\r\ncv.Split(hsv_img, tmp_img, None, None, None)<\/pre>\n<figure id=\"attachment_1825\" aria-describedby=\"caption-attachment-1825\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1825\" title=\"roz-prepyr\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1825\" class=\"wp-caption-text\">componenta Hue (1 canal)<\/figcaption><\/figure>\n<p>Apoi, am g\u0103sit pe un site pe care acum nu \u00eel mai g\u0103sesc, am \u00eenv\u0103\u021bat o \u0219mecherie, pentru a reduce din zgomotul din imagine, se face down-sampling \u0219i dup\u0103 up-sampling, \u0219i din aproxima\u021biile f\u0103cute, chestiile micu\u021be dispar :)<\/p>\n<p>&nbsp;<\/p>\n<pre lang=\"python\"># ne trebuie o imagine suplimentara - in care se stocheaza temporar imaginea down-sampled\r\ntmp2 = cv.CreateImage((cv.GetSize(img)[0] \/ 2, cv.GetSize(img)[1] \/ 2), IPL_DEPTH_8U, 1)\r\n# in jos\r\ncv.PyrDown(tmp_img, tmp2)\r\n# in sus\r\ncv.PyrUp(tmp2, tmp_img)<\/pre>\n<figure id=\"attachment_1826\" aria-describedby=\"caption-attachment-1826\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1826\" title=\"roz-postpyt\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1826\" class=\"wp-caption-text\">dup\u0103 resampling<\/figcaption><\/figure>\n<p>Apoi, cu imaginea asta de un canal rezultata, in care o parte din zgomot a fost scoas\u0103, \u00eencerc\u0103m s\u0103 g\u0103sim petele de culoare care se potrivesc cu culoarea noastr\u0103. De notat c\u0103 imaginea asta are un singur canal, de la 0 la 255, \u0219i de asemenea de notat c\u0103, \u00een func\u021bie de lumiozitate \u0219i al\u021bi asemenea factori s-ar putea s\u0103 fie nevoie s\u0103 l\u0103\u021bim un pic intervalul de c\u0103utare.<\/p>\n<pre lang=\"python\"># in imaginea color_mask vom pastra practic o imagine binara (0 = punctul nu e in interval, 255 = e in interval)\r\ncolor_mask = cv.CreateImage(cv.GetSize(img), IPL_DEPTH_8U, 1)\r\n# functia InRangeS va binariza practic imaginea, si tot ce e intre 150 - 174 va iesi 255, restul 0\r\ncv.InRangeS(tmp_img, cv.Scalar(150), cv.Scalar(174), color_mask)<\/pre>\n<figure id=\"attachment_1827\" aria-describedby=\"caption-attachment-1827\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1827\" title=\"roz-range\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1827\" class=\"wp-caption-text\">binarizare - a se vedea ca nu doar baliza noastra este identificat\u0103 ca roz<\/figcaption><\/figure>\n<p>Ok, \u0219i avem acum o imagine binarizat\u0103, care poate con\u021bine unul sau mai multe obiecte care s-au potrivit pe ce c\u0103utam noi. Acum am vrea s\u0103 vedem dac\u0103 g\u0103sim obiectul nostru &#8211; putem c\u0103uta un p\u0103trat (4 laturi, convex), dar \u00een cazul nostru am preferat s\u0103 c\u0103utam cel mai mare poligon de culoarea respectiv\u0103.<\/p>\n<p>&nbsp;<\/p>\n<p>Ca s\u0103 facem asta, putem folosi o <a href=\"http:\/\/opencv.willowgarage.com\/documentation\/python\/imgproc_structural_analysis_and_shape_descriptors.html?highlight=findcontours#FindContours\">func\u021bie<\/a> care caut\u0103 contururi \u00een imaginea noastr\u0103 binar\u0103. Apoi putem trece prin toate contururile, si incercam sa gasim un maxim intre ele, care va fi obiectul nostru. Mai folosim un pic de OpenCV magic, functia <a href=\"http:\/\/opencv.willowgarage.com\/documentation\/python\/imgproc_structural_analysis_and_shape_descriptors.html?highlight=findcontours#approxchains\">ApproxPoly<\/a>, care face un fel de interpolare \u0219i mai reduce din zgomot, \u00een func\u021bie de coeficientul pe care \u00eel prime\u0219te func\u021bia.<\/p>\n<pre lang=\"python\">cv.NamedWindow(\"show\")\r\n\r\nstorage = cv.CreateMemStorage(0)\r\n# cautam in imaginea binarizata contururi\r\ncontours = cv.FindContours(color_mask, storage, mode = CV_RETR_LIST, method = CV_CHAIN_APPROX_NONE)\r\n# pregatim color_mask, care acum este reciclabila, pentru a afisa in ea contururile\r\ncv.Zero(color_mask)\r\n\r\n# initializari\r\nc = contours\r\nindex = 0\r\nmax_area = 0\r\nmax_result = None\r\n\r\nif len(contours):\r\n    while c.h_next() != None:\r\n        current = c\r\n        c = c.h_next()\r\n\r\n        # cautam cel mai mare poligon\r\n        if cv.ContourArea(current) &gt; max_area:\r\n            result = cv.ApproxPoly(current, storage, CV_POLY_APPROX_DP, cv.ArcLength(current) * 0.02, 0);\r\n            max_result = result\r\n            max_area = cv.ContourArea(current)\r\n\r\n    if not max_result is None:\r\n        # desenam contururile\r\n        cv.DrawContours(color_mask, max_result, cv.ScalarAll(255), cv.ScalarAll(128), 2, thickness = -1)\r\n        # si le afisam\r\n        cv.ShowImage(\"show\", color_mask)\r\n        # si asteptam sa se apese ceva, ca sa vedem rezultatul\r\n        cv.WaitKey()<\/pre>\n<figure id=\"attachment_1828\" aria-describedby=\"caption-attachment-1828\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result5.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1828\" title=\"roz-result5\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result5-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result5-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result5.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1828\" class=\"wp-caption-text\">\u0219i rezultatul final<\/figcaption><\/figure>\n<p>\u00cenc\u0103 un set de imagini:<\/p>\n<figure id=\"attachment_1833\" aria-describedby=\"caption-attachment-1833\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz4.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1833\" title=\"roz4\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz4-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz4-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz4.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1833\" class=\"wp-caption-text\">Original<\/figcaption><\/figure>\n<figure id=\"attachment_1832\" aria-describedby=\"caption-attachment-1832\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1832\" title=\"roz-prepyr\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr1-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr1-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-prepyr1.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1832\" class=\"wp-caption-text\">Componenta Hue<\/figcaption><\/figure>\n<figure id=\"attachment_1830\" aria-describedby=\"caption-attachment-1830\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1830\" title=\"roz-postpyt\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt1-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt1-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-postpyt1.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1830\" class=\"wp-caption-text\">Resample<\/figcaption><\/figure>\n<figure id=\"attachment_1829\" aria-describedby=\"caption-attachment-1829\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1829\" title=\"roz-range\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range1-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range1-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-range1.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1829\" class=\"wp-caption-text\">binarizare (inranges)<\/figcaption><\/figure>\n<figure id=\"attachment_1831\" aria-describedby=\"caption-attachment-1831\" style=\"width: 300px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result51.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1831\" title=\"roz-result5\" src=\"http:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result51-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result51-300x225.jpg 300w, https:\/\/yeti.albascout.ro\/blog\/wp-content\/uploads\/2011\/05\/roz-result51.jpg 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-1831\" class=\"wp-caption-text\">rezultat final<\/figcaption><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Ziceam c\u0103 voi reveni cu un mic post despre cum se poate face quick&#8217;n&#8217;dirty color tracking cu OpenCV \u0219i python. De ce OpenCV? Pentru c\u0103 are tot ce-i trebuie. De ce python? Pentru c\u0103 pentru explica\u021bii \u0219i pentru fast prototyping nu cred ca este alternativ\u0103 mai bun\u0103 (e u\u0219or de citit, u\u0219or de scris, chiar &hellip; <a href=\"https:\/\/yeti.albascout.ro\/blog\/color-tracking-cu-opencv-si-python\/\" class=\"more-link\">Continu\u0103 s\u0103 cite\u0219ti <span class=\"screen-reader-text\">Color tracking cu OpenCV \u0219i python<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[564,557],"tags":[727,731,733,809],"class_list":["post-1788","post","type-post","status-publish","format-standard","hentry","category-roboti","category-facultate","tag-cs-pub","tag-eurobot2011","tag-opencv","tag-roboti"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts\/1788","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/comments?post=1788"}],"version-history":[{"count":8,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts\/1788\/revisions"}],"predecessor-version":[{"id":1835,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts\/1788\/revisions\/1835"}],"wp:attachment":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/media?parent=1788"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/categories?post=1788"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/tags?post=1788"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}