Archyvas

September, 2011 archyvas

Objektų žymėjimas

2011-09-29

Objektų žymėjimas (connected components labeling, objects labeling) yra atliekamas visus objektus vieną nuo kito išskiriant, pavyzdžiui nudažant skirtinga spalva arba priklijuojant etiketę (label). Išbandysim abu šiuos variantus.

Paimkim binarinį paveiksliuką:

Algoritmas (recursive labeling):

  • skenuojamas visas paveiksliukas ieškant juodos spalvos ir jeigu tokia rasta – nuspalvosime išskirtine intensyvumo spalva (pilku atspalviu);
  • sudaromas rasto taško kaimynų sąrašas ir pas kiekvieną jo kaimyną apsilankoma, nudažant juos visus vienodu intensyvumu. Lankyti kaimynus galima su rekursija, tuomet visi objekto taškai bus tikrai apeiti ir pažymėti;
  • ieškomas naujas objektas.

Programos kodas žemiau:

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
tic;
clc;
close all;
file = 'D:\objektai.bmp';
rgb = imread(file);
image(rgb);
global I;
I = rgb2gray(rgb);
label = 0;
labelDelta = 20;
[MaxRow, MaxCol] = size(I);
% set(0,'RecursionLimit',1500); didesniems objektams gali reikėti didinti
% rekursijų kiekio limitą
for L = 1 : MaxRow
    for P = 1 : MaxCol
        if I(L, P) == 0 % aptiktas objektas
            label = label + labelDelta; % kiekvienam objektui kitoks intensyvumas
            search(label, L, P); % nuspalvoti visus objekto pikselius vienodu intensyvumu
        end;
    end;
end;
imshow(I);
disp(toc);

Rekursija apeinanti visus kaimynus:

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
function [] = search(label, L, P)
global I;
I(L, P) = label; % keičiamas pikselio intensyvumas
[Nset] = neighbors(I, L, P); % randami 4 pikselio kaimynai
s = size(Nset);
n = s(3);
for i = 1 : n % kiekvienas kaimynas tikrinamas atskirai
    value = Nset(:, :, i);
    if value(3) == 0
        search(label, value(1), value(2));
    end;
end;

Galima rasti ir 8 ir 4 kaimynus. Šiuo atveju randami tik keturi:

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function [res] = neighbors(I, L, P)
[MaxRow, MaxCol] = size(I);
res = [];
cur = -1;
if L > 1 % viršutinis kaimynas
    cur = cur + 1;
    res(:, :, cur + 1) = int16([L - 1 P int16(I(L - 1, P))]);
end;
if L < MaxRow % apatinis kaimynas
    cur = cur + 1;
    res(:, :, cur + 1) = int16([L + 1 P int16(I(L + 1, P))]);
end;
if P > 1 % kairysis kaimynas
    cur = cur + 1;
    res(:, :, cur + 1) = int16([L P - 1 int16(I(L, P - 1))]);
end;
if P < MaxCol % dešinysis kaimynas
    cur = cur + 1;
    res(:, :, cur + 1) = int16([L P + 1 int16(I(L, P + 1))]);
end;

Apdorojus pirmąjį paveiksliuką:Algoritmas bėga per visus pikselius nuo viršaus apačion ir nuo kairės dešinėn, todėl pirmiausia aptinkamas trečias objektas ir po to visi kiti. Rezultate matosi kaip kiekvieno objekto intensyvumas skiriasi.

Pabandom lygiai tą patį padaryti su paveiksliuku, kurį naudojom skaičiuojant objektus, tik intensyvumo pokytį pakeičiam į 4, nes objektų gerokai daugiau ir reiktų, kad visi sutilptų į 255 reikšmes:

Labiausiai skiriasi vienas nuo kito objektai, kurie buvo pažymėti pirmiausiai ir paskiausiai. Tokį paveikslą gali būti sunku naudoti, nes nėra kaip pasakyti apie kurias ląsteles norima diskutuoti, juk nesakysi, kad „ląstelė, kurios intensyvumas 74 % …“, todėl geriausia, jei kiekvienam objektui suteiksim identifikatorius. Kodas žemiau (funkcijos nekeistos):

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
tic;
clc;
close all;
file = 'D:\FROG BLOOD binary.bmp';
rgb = imread(file);
file2 = 'D:\FROG BLOOD 402 rgb.bmp';
rgb2 = imread(file2);
imshow(rgb2);
global I;
I = rgb2gray(rgb);
label = 0;
labelDelta = 4;
[MaxRow, MaxCol] = size(I);
% set(0,'RecursionLimit',1500); didesniems objektams gali reikėti didinti
% rekursijų kiekio limitą
for L = 1 : MaxRow
    for P = 1 : MaxCol
        if I(L, P) == 0 % aptiktas objektas
            label = label + labelDelta; % kiekvienam objektui kitoks intensyvumas
            search(label, L, P); % nuspalvoti visus objekto pikselius vienodu intensyvumu
            text('units','pixels','position',[P MaxRow-L],'fontsize',7,'string',num2str(label/labelDelta),'BackgroundColor',[0.9 0.9 0.9])
        end;
    end;
end;
% imshow(I);
disp(toc);

Rezultatas:

Visi 59 objektai pažymėti.

Šio algoritmo didžiausias trūkumas, kad netinka dideliems objektams, nes tada reikia didinti rekursijų kiekio limitą. O ir padidinus ne visada to pakanka, nes viršijus Matlab galimybes, jis lūžta. Tačiau šiai problemai spręsti yra ir daugiau algoritmų: “row-by-row labeling”, naudojant “union-find structure” arba “run-length encoding” ir kt.

Tyrinėjimai , , , ,

Objektų skaičiavimas paveiksliukuose

2011-09-23

Ką gi, šiuo įrašu startuoju naujos nišos gyvavimą. Abejoju ar tokia Lietuvoje jau yra, net netikrinau kol kas, bet bus įdomu paieškoti. Neskubėsiu klijuoti etiketės apie ką bloginsiu, tegu tai paaiškėja po truputi, su laiku. „Tyrinėjimai. Kūryba. Eksperimentai.“ yra labai taiklus apibūdinimas, tegu taip ir lieka.

Išbandysime vieną labai paprastą idėją, kurios pagalba galima skaičiuoti objektus paveiksliukuose (objects counting in binary pictures). Paimkime binarinį paveiksliuką, kuriame yra 7 objektai.

Vaizdas gerokai išdidintas, kad bloge objektai matytųsi aiškiai, kiekvienas objekto pikselis matomas kaip maždaug 14×14 pikselių dydžio juoda sritis. Kaip šiuos juodus objektus suskaičiuoti programiškai? Galime pasitelkti vieną įdomią savybę, kad skirtumas tarp išorinių objekto kampų ir vidinių visada yra 4. Kad būtų lengviau įsivaizduoti, nuspalvokime objektus pilkai iš pažymėkime išorinius kampus raudonai, o vidinius mėlynai.

Pavyzdžiui pirmas objektas turi keturis išorinius kampus ir nulį vidinių. Antras objektas 8 išorinius ir 4 vidinius. Trečias objektas 9 išorinius ir 5 vidinius. Vizualiai išorinis kampas matomas kaip juodas (šiuo atveju pilkas), o vidinis kaip baltas. Tuomet suskaičiavus visus paveiksliuke esančius išorinius kampus ir vidinius, o jų skirtumą padalinus iš 4, turėtume gauti objektų skaičių. Konkrečiai šiame paveiksliuke būtų (39-11)/4=28/4=7. Iš viso septyni objektai.

Algoritmas gana paprastas. Pradžioje paveiksliukas paverčiamas į binarinį, kad būtų tik dvi intensyvumo reikšmės, arba 0 (juoda) arba 1 (balta). Tada pereinami visi taškai paeiliui tikrinant kokia kiekvieno taško srityje yra 2×2 matrica. Jei tarkim [0 1;1 1], [1 0; 1 1], [1 1; 0 1] arba [1 1; 1 0] (visais atvejais elementų suma yra 3), tuomet tai yra išorinis kampas. Jei [1 0; 0 0], [0 1; 0 0], [0 0; 1 0] arba [0 0; 0 1] (visais atvejais elementų suma 1), tuomet tai yra vidinis kampas.

Selec All Code:
1
2
3
4
5
6
7
8
clc;
close all;
file = 'D:\holes.bmp';
rgb = imread(file);
image(rgb); % patikriname kaip atrodo pradinis vaizdas
BW = im2bw(rgb, 0); % domina binarinis vaizdas
[objects, E, I] = count_objects(BW);
fprintf('Iš viso objektų: %d, E: %d, I: %d\n', objects, E, I);

Objektų skaičiavimo funkcija:

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function [result, E, I] = count_objects(BW)
[MaxRow, MaxCol] = size(BW);
E = 0; % išoriniai kampai
I = 0; % vidiniai kampai
for L = 1 : MaxRow - 1
  for P = 1 : MaxCol - 1
    M = BW(L : L + 1, P : P + 1);
    S = sum(M(:));
    if S == 3
      E = E + 1;
    end;
    if S == 1
      I = I + 1;
    end;
  end;
end;
result = (E - I) / 4;

Paimkime vizualiai gerokai sudėtingesnius atvejus:

Programa gauna 9 objektus. Tiek jų ir yra. Nors objektai yra sudėtingi, bet mažai siejasi su realiu gyvenimu, todėl pabandykime paimti ką nors tikro, pavyzdžiui štai tokį vaizdą:

Vaizdas yra spalvotas ir iš realaus gyvenimo. Kaip suskaičiuoti šiuos objektus? Lygiai taip pat, tik pradžiai padidiname šviesumą ir ryškumą, kad atmesti šviesesnes sritis ir konvertuojame į binarinį paveiksliuką:

Kodas keičiasi nežymiai:

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
clc;
close all;
file = 'D:\FROG BLOOD 402.jpg';
rgb = imread(file);
rgb = imadjust(rgb, [0 0.4], [0.6 1]);
image(rgb); % patikriname kaip atrodo pradinis vaizdas
BW = im2bw(rgb, 0.9); % domina binarinis vaizdas
[m, n] = size(BW);
N = ones(m + 2, n + 2);
N(2 : m + 1, 2 : n + 1) = BW;
imshow(N);
[objects, E, I] = count_objects(N);
fprintf('Iš viso objektų: %d, E: %d, I: %d\n', objects, E, I);

O rezultatas yra: „Iš viso objektų: 59, E: 1287, I: 1051“. Viso 59 objektai, jų skaičiuoti rankomis jau visai nesinorėtų.

Tiesa, algoritmas neveiks tais atvejais, jei skaičiuojamuose objektuose yra fono spalvos arba objektas nėra apsuptas fono spalva (dėl to paskutinis paveiksliukas padidintas į visas puses per vieną pikselį taip išplečiant baltą foną), todėl algoritmas netobulas, tačiau labai paprastas ir įdomus.

Tyrinėjimai , ,

Kaip mokytis kalbų? III dalis. Subtitrų išvalymo skriptas

2011-09-19

Šis įrašas yra atsakymas į Gintaro klausimą: kur rasti subtitrų išvalymo skriptą? Ir atsakymas toks: šiame įraše!

Bet pradžiai kelios mintys kam gi tas skriptas reikalingas… Vienas iš geriausių kalbos mokymosi priemonių yra serialų žiūrėjimas ir klausimas (filmai irgi neblogai). Esu tikras, kad kiekvienas gali atrasti sau tinkamą serialą. O kai atrandi, turi net kelis sezonus puikaus turinio klausymui. Tačiau vien klausimo nepakanka, reiktų galimybės peržiūrėti ką aktoriai kalba ir tam puikiausiai tinka subtitrai. Problema atsiranda tada, kai norisi subtitrus importuoti į LingQ sistemą ir juose labai daug “šiukšlių”, rankomis valyti nepatogu, užima daug laiko. Būtent čia skriptas ir naudingas, jis išvalo subtitrus nuo “šiukšlių”, pavyzdžiui tokių: “00:00:04,612 –> 00:00:05,727″.

Žemiau yra veikiantis išvalymo skriptas. Paprasčiausiai nukopijuokit subtitrų tekstą iš parsisiųsto SRT failo į pirmąjį lauką ir paspauskit “Išvalyti” mygtuką. Antrame lauke turėtų atsirasti švarus tekstas, kurį jau galima importuoti.

Subtitrai:

Švarus tekstas:


Skriptas labai paprastas, tereikia surasti ir atmesti nereikalingas eilutes. Jei turit poreikį modifikuoti, kodas žemiau.

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (isset($_POST['text']))
  $text = "\n" . $_POST['text'];
else
  $text = '';
$text = strip_tags($text);
$records = explode("\n\r", $text);
foreach ($records as $record)
{
  $lines = explode("\n", $record);
  unset($lines[1]);
  unset($lines[2]);
  $record = implode($lines, "\n");
  echo $record;
}

Kiek tikrinau, turėtų veikti. Bet jei iškils kokių problemų, duokit žinot komentaruose.

Kūryba , , ,

Blogo likimas

2011-09-18

Noriu padėkoti visiems, kurie prenumeravo šį blogą, komentavo, dalinosi savo patyrimais ir emocijom. Ypatingai žaviuosi tais, kurie pritaikė pateiktas idėjas pagerindami savo ir aplinkinių gyvenimus, pasidalino rezultatais. Ačiū jums! Malonu žinoti, kad yra bendraminčių.

Daugiausiai populiarumo susilaukė įrašas apie tai, kaip laimėti kovą su tarakonais. Tikrai nesitikėjau, kad ši problema tokia svarbi ir sudarys liūto dalį viso srauto.

Ir dabar jau tikriausiai tikitės atsisveikinimo žodžio, gerų palinkėjimų ir blogo uždarymo. Ne, to nebus! Man prasideda įdomus ir naujas gyvenimo periodas, kuriame ketinu atrasti vietos bloginimui. Tačiau blogas jau nebus toks pat kaip buvo.

Dalį įrašų pašalinsiu, o kai kuriuos plėtosiu ir toliau. Jei jus domina GTD, kalbų mokymasis, saviugdos temos, tai esu tikras, kad tokio pobūdžio įrašų dar bus. Gal net dažniau nei, kad būdavo. Turiu naują gyvenimo normą, kad kiekvienais metais, vieną pasirinktą gyvenimo sritelę patobulinti iš esmės, negrįžtamai ją pakeisti į gerąją pusę, kad paprasčiausiai neliktų kelio atgal. Pirma sėkmė buvo su anglų kalba, antroji su GTD. Šiais metais ir vėl radau labai įdomią sistemą, kuri jau pasiteisino, bet apie ją galbūt vėliau. Todėl įvairių įspūdžių tikrai dar bus, tik pernelyg asmeninių temų jau neliesiu, paliksiu jas žmonėms su kuriais bendrauju artimiau.

Nuo šiol blogas bus nišinis. Turiu mane intriguojančią viziją ir bandysiu ją paversti tikrove. Rizikuosiu ir pažiūrėsim kas iš to išeis.

Kūryba ,