Digitální zpracování obrazu

Antonín Slavík
Matematicko-fyzikální fakulta UK

Zpracování obrázků v Mathematice

Více možností než v běžných grafických editorech

Hromadné zpracování sady obrázků

Přímý přístup k datové reprezentaci obrazu, možnost implementace vlastních algoritmů

Zpracování obrazu jako součást složitějších programů

Reprezentace bitmapových obrázků

Jeden nebo více barevných kanálů

Různé barevné modely: Grayscale, RGB, CMYK, HSB (hue, saturation, brightness)

Každý barevný kanál lze chápat jako matici, jejíž prvky mají obvykle některý z následujících typů:

({{Bit, 0 nebo 1}, {Byte, celé číslo 0 až 255}, {Bit16, celé číslo 0 až 65535}, {Real, reálné číslo z intervalu [0,1]}})

Obrázky s alfa kanálem (průhlednost)

Bitmapové obrázky v Mathematice

Datový typ Image

Bitmapové obrázky jsou reprezentovány jako objekty typu Image.

Funkce Import vrací Image:

lena=Import["http://www.karlin.mff.cuni.cz/~slavik/pokrocili_ls_2020_21/lena.jpg"]

FullForm[lena]

Lze použít také drag&drop:

spikey=;

FullForm[spikey]

Výroba Image zadáním matice

Pořadí řádků je shora dolů:

Image[{{0,0.2,0.4},{0.6,0.8,1}}]

Image[{{{1,0,0},{0,1,0},{0,0,1}},{{0,1,1},{1,0,1},{1,1,0}}}]

Image[{{{255,0,0},{0,255,0},{0,0,255}},{{0,255,255},{255,0,255},{255,255,0}}},"Byte"]

Přípustné typy jsou Bit, Byte, Bit16, Real.

Převod grafiky na bitmapový obrázek

smiley=Graphics[{{Yellow,EdgeForm[Black],Disk[{5,5},4]},{Disk[{3.5,6.5},0.45],Disk[{6.5,6.5},0.45],Circle[{5,5.5},3,{225*Degree,315*Degree}],Line[{{5,5.5},{4.5,4.5},{5,4.5}}]}},ImageSize->Tiny]

Image[smiley]

FullForm[%]

Požadované rozměry lze zadat pomocí ImageSize:

Image[smiley,ImageSize->{50,50}]

Získávání informací o obrázcích

lena

ImageDimensions[lena]

ImageChannels[lena]

ImageType[lena]

Options[lena]

ImageData implicitně vrací hodnoty typu Real:

ImageData[lena]

Můžeme ale požádat o libovolný datový typ (Bit, Byte, Bit16, Real):

ImageData[lena,"Byte"]

Převod do jiného barevného modelu (RGB, Grayscale, CMYK, HSB):

graylena=ColorConvert[lena,"GrayScale"]

ImageData[graylena]

ImageHistogram[lena, Appearance->"Separated",FrameTicks->{True,False}]

Geometrické transformace

Získání výřezu, změna velikosti

vanEyck=Import["http://www.karlin.mff.cuni.cz/~slavik/pokrocili_ls_2020_21/van-eyck.jpg"]

zrcadlo=ImageTake[vanEyck,{1000,1520},{1300,1820}] (* řádky 1000 až 1520, sloupce 1300 až 1820 *)

Řádky jsou číslovány shora, sloupce zleva. Čísla řádku a sloupce na pozici kurzoru myši lze zjistit pomocí kontextového menu (Get Pixel Color).

ImageDimensions[zrcadlo]

ImageResize[zrcadlo, Scaled[1/2]]

ImageResize[zrcadlo, 100]

ImageResize[zrcadlo,{200,100}]

Doplnění okrajů

Manipulate[ImagePad[lena, numPixels,padding],
 {{numPixels,40},1,400,1, Appearance->"Labeled"},{padding,{Black,Blue,"Fixed","Periodic", "Reflected"}}]

Rotace, zrcadlení

Manipulate[ImageRotate[lena,α Degree,Background->White],{α,0,360}]

ImageReflect[lena, Left-> Right]

Obecné (nelineární) transformace

In (RefLink[ImageForwardTransformation, paclet:ref/ImageForwardTransformation])[image, f] every pixel at position {x,y} in image is placed at position f[{x,y}] in the output image. This is known as a forward transformation.

In (RefLink[ImageTransformation, paclet:ref/ImageTransformation])[image,f], the value of every pixel at position {x,y} in the output image is obtained from the position f[{x,y}] in the input image. This is known as a backward transformation.

grid=;

RotationTransform[α][{x,y}]

ImageForwardTransformation[grid,RotationTransform[10°],DataRange->{{-1,1},{-1,1}}]

ImageTransformation[grid,RotationTransform[10°],DataRange->{{-1,1},{-1,1}}]

ImageTransformation je obvykle rychlejší a výsledek je kvalitnější než u ImageForwardTransformation:

ImageForwardTransformation[grid,RotationTransform[10°],DataRange->{{-1,1},{-1,1}}]//Timing

ImageTransformation[grid,RotationTransform[-10°],DataRange->{{-1,1},{-1,1}}]//Timing

DataRange nastavuje soustavu souřadnic ve výchozím obrázku (výchozí nastavení: spodní hrana obrázku splývá s intervalem [0,1] na ose x). Pomocí PlotRange lze nastavit oblast obrázku po transformaci, která bude zobrazena.

ImageTransformation[grid,RotationTransform[-10°],DataRange->{{-1,1},{-1,1}},PlotRange->All]

DataRange->Full zvolí souřadnice ve výchozím obrázku tak, že levý dolní roh je v počátku a 1 pixel = čtverec o straně 1.

lena=;

Posunutí doprava o polovinu šířky obrázku:

ImageForwardTransformation[lena,TranslationTransform[{.5,0}]]

Posunutí doprava o 20 pixelů:

ImageForwardTransformation[lena,TranslationTransform[{20,0}],DataRange->Full]

Přičtení sinusoidy k y-ové souřadnici:

Manipulate[ImageTransformation[lena,{#[[1]],#[[2]]+a Sin[b#[[1]]+c]}&,DataRange->{{0,2Pi},{-1,1}},PlotRange->{{0,2Pi},{-1.5,1.5}}],{{a,0.25,"amplituda"},0,0.5,Appearance->"Labeled"},{{b,1,"frekvence"},1/2,3,Appearance->"Labeled"},{{c,0,"fáze"},0,2Pi,Appearance->"Labeled"}]

Některé transformace se snáze vyjádří v polárních souřadnicích.

Kartézské a polární souřadnice: x=r cos α, y=r sin α.

„Rybí oko”: Bod, který má polární souřadnice (r, α), se zobrazí do bodu s polárními souřadnicemi ((Sqrt[r]), α).

f[{x_,y_}]:={r^2*Cos[α],r^2*Sin[α]}/.{r->Norm[{x,y}],α->ArcTan[x,y]};
ImageTransformation[grid,f,DataRange->{{-1,1},{-1,1}}]

Obecnější verze s nastavitelným exponentem:

Manipulate[ImageTransformation[lena,{r^a*Cos[α],r^a*Sin[α]}/.{r->Norm[#],α->ArcTan@@#}&,DataRange->{{-1,1},{-1,1}}],{{a,1},1/2,2,Appearance->"Labeled"}]

Spirálový efekt:

Manipulate[ImageTransformation[lena,{r*Cos[α+a r],r*Sin[α+a r]}/.{r->Norm[#],α->ArcTan@@#}&,DataRange->{{-1,1},{-1,1}}],{a,0,10,Appearance->"Labeled"}]

Projektivní transformace

Obecný tvar projektivní transformace v (ℝ^2):

LinearFractionalTransform[(({{(Subscript[a, 1]), (Subscript[a, 2]), (Subscript[a, 3])}, {(Subscript[b, 1]), (Subscript[b, 2]), (Subscript[b, 3])}, {(Subscript[c, 1]), (Subscript[c, 2]), (Subscript[c, 3])}}))][{x,y}]

(V homogenních souřadnicích je bod v (ℝ^2) popsán trojicí čísel a projektivní transformace je lineární zobrazení s příslušnou maticí.)

ImagePerspectiveTransformation[,(({{0.9, 0.1, 0}, {0.3, 0.9, -0.1}, {0, 0.1, 1}}))]

Odstranění perspektivního zkreslení:

ImagePerspectiveTransformation[,(({{1, 0, 0}, {0, 1, 0}, {0, -(1/320), 1}})),200,DataRange->Full,PlotRange->{{-100,900},{0,1500}}]

Analýza Holbeinova obrazu Vyslanci (zdroj: Wolfram Blog, Andrew Moylan: The Ambassadors):

img=Import["http://www.karlin.mff.cuni.cz/~slavik/pokrocili_ls_2020_21/ambassadors.jpg"]

ImagePerspectiveTransformation[img,(({{0.5, -0.87, -0.05}, {-1.34, 2.7, 0.43}, {-0.62, 0.2, 0.525}})),400]

ImageDimensions[img]

Clear[img]

Základní operace s obrázky

Úpravy jasu, kontrastu a gama korekce

balony=;

Automatická úprava jasu, kontrastu a gama korekce:

ImageAdjust[balony]

Ruční nastavení jasu, kontrastu a gama korekce:

Manipulate[ImageAdjust[balony,{kontrast,jas,gama}],{{kontrast,0},-1,1,Appearance->"Labeled"},{{jas,0},-1,1,Appearance->"Labeled"},{{gama,1},0.1,3,Appearance->"Labeled"}]

Correction for contrast, brightness and gamma correction can take any of the following settings:

({{      , c, adjusts the image contrast by c}, {      , {c,b}, also adjusts the image brightness by b}, {      , {c,b,γ}, also performs a gamma correction by raising the values to the power of γ}})

The correction transformation applied to every pixel value x is (b+1) (c+1) (x^γ)-c/2, rounded or clipped to the image type, if necessary.
Zero adjustment corresponds to {0,0,1} correction.

Oddělení a sloučení barevných kanálů

ColorSeparate[balony]

RotateRight[%]

ColorCombine[%]

Prahování

i=Image[Graphics[{GrayLevel[0.3],Disk[{0,0}],GrayLevel[0.5],Disk[{3,0}],GrayLevel[0.7],Disk[{6,0}]}]]

Binarize převede všechny pixely zadaného obrázku na bílou nebo černou podle toho, zda jejich hodnoty jsou větší nebo menší než zadaný práh. (Barevný obrázek je nejprve převeden na stupně šedi).

Manipulate[Binarize[i,prah],{prah,0,1,Appearance->"Labeled"}]

Spojení obrázků

Položení obrázků „přes sebe“ (vhodné pro poloprůhledné obrázky)

text=Graphics[{Opacity[0.6],White,Text[Style["Classified",FontWeight->Bold,FontFamily->"Times",FontSize->50],{0,0},{0,0},{Cos[45°],Sin[45°]}]},Background->None];

ImageCompose[lena, Image[text]]

Položení obrázků „vedle sebe“:

Table[ImageAdjust[lena,{kontrast,jas}],{jas,0,.5,.5},{kontrast,0,1,.25}]

ImageAssemble[%]

Aritmetické operace

Sčítání (ImageAdd), odčítání (ImageSubtract), násobení (ImageMultiply)

Výsledky aritmetických operací jsou vždy oříznuty tak, aby ležely v předepsaném intervalu (např. [0,1]).

Příklad: Plynulý přechod pozadí do černé

pav=;

maska=DensityPlot[Exp[-0.3Norm[{x,y}]^2],{x,-2,2},{y,-2,2},ColorFunction->GrayLevel,Frame->False,PlotRangePadding->None]

ImageMultiply[pav,Image[maska]]

Příklad: Obrázek s texturou (zdroj: Wolfram Blog, Image Effects in Mathematica)

textury={,,,,,,,,,};

Map[ImageAdjust[ImageMultiply[balony,#1]]&,textury]

Příklad: Vybělení oblohy

brno1=;

modra=ColorSeparate[brno1][[3]]

Binarize[modra,0.85]

ImageAdd[%,brno1]

Filtry

Detekce hran a jejich zvýraznění (zaostření)

born=;

EdgeDetect[born]

GradientFilter[born,1]

Manipulate[Sharpen[born,r],{r,0,10}]

Rozmazání

Manipulate[Blur[,blur],{blur,0,8}]

The elements of the image "All is Vanity" by Charles Allen Gilbert are arranged to present a second, more subtle image. By blurring the image so that the fine details are less distracting, the viewer perceives the second image more clearly.

THIS NOTEBOOK IS THE SOURCE CODE FROM
Effect of Detail on Visual Perception from The Wolfram Demonstrations Project
http://demonstrations.wolfram.com/EffectOfDetailOnVisualPerception/
Contributed by: Jon McLoone

Příklad: Grafika doplněná stínem

DropShadow[x_,ds_: {-5,5},blur_:5]:=Graphics[{Inset[Blur[x,blur],Center,Offset[ds,ImageScaled[{.5,.5}]],Scaled[{1,1}]],Inset[x,Center,Center,Scaled[{1,1}]]}]

txt=Text[Style["TEXT SE STÍNEM",FontSize->24,FontFamily->"Source Serif Pro"]]

DropShadow[Graphics[txt],{-7,7},7]

caffeine=Show[ChemicalData["Caffeine","CHColorStructureDiagram"],ImageSize->Medium]

DropShadow[caffeine,{-7,7},7]

Vyhlazování a průměrování se zachováním hran

Třída filtrů MeanShiftFilter, KuwaharaFilter, BilateralFilter, PeronaMalikFilter, CurvatureFlowFilter, ...

Příklad: Rembrandtův autoportrét, olejomalba s mnoha prasklinami

rembrandt=Import["http://www.karlin.mff.cuni.cz/~slavik/pokrocili_ls_2020_21/rembrandt.jpg"]

Použití Blur: Defekty částečně potlačeny za cenu rozmazání obrazu

Blur[rembrandt,5]

Použití KuwaharaFilter: Defekty méně viditelné, obraz ostřejší

KuwaharaFilter[rembrandt,2]

Registrace snímků

ImageAlign

Registrace je transformace jednoho snímku do soustavy souřadnic jiného snímku. Registrované snímky se musejí částečně překrývat, mohou být ale pořízeny z různých stanovišť, za odlišných světelných podmínek, apod.

Některé aplikace: porovnávání obrázků, spojování obrázků

?ImageAlign

ImageAlign[, ]

ImageAlign[,]

ImageAlign can take a “Transformation” option. Possible transformations in order of increasing complexity:

({{      , "Translation", translation only}, {      , "Rigid", translation and rotation}, {      , "Similarity", translation, rotation, and scaling}, {      , "Affine", linear transformation and translation}, {      , "Perspective", linear fractional transformation}})

With the default setting “Transformation”->Automatic, ImageAlign attempts to find the simplest possible transformation.

brno1=;brno2=;

Neúspěšný pokus o registraci:

ImageAlign[brno1,brno2]

Příčinou neúspěchu jsou příliš velké rozdíly v jasu.

brno1adjusted=ImageAdjust[brno1,{0,.9}]

aligned=ImageAlign[brno1adjusted,brno2,Method->{"Keypoints",{"ORB","KAZE"}}]

Manipulate[ImageCompose[aligned,{brno1,α}],{{α,0.5},0,1}]

FindGeometricTransform

?FindGeometricTransform

{e,t}=FindGeometricTransform[brno1adjusted,brno2]

ImagePerspectiveTransformation[brno2,t,DataRange->Full,Padding->White]

Příklad použití: Spojení snímků

i1=;i2=;

tr=FindGeometricTransform[i1,i2][[2]]

{w,h}=ImageDimensions[i2];
i2transformed=ImagePerspectiveTransformation[i2,tr,DataRange->Full,Padding->White,PlotRange->{{0,First@tr[{w,0}]},{0,h}}]

Manipulate[ImageCompose[i2transformed,{i1,α},Round@({w,h}/2)],{{α,1},0,1},SaveDefinitions->True]

ImageCorrespondingPoints

Algoritmus registrace snímků je založen na hledání referenčních bodů v obrázcích a jejich vzájemné korespondence:

?ImageCorrespondingPoints

{pts1,pts2}=ImageCorrespondingPoints[i1,i2]

GraphicsRow[{Show[i1,Graphics[{Red,Point[pts1]}]],Show[i2,Graphics[{Red,Point[pts2]}]]}]

FindGeometricTransform[pts1,pts2]

Morfologické operace

Morfologické operace pracují s černobílými obrázky (bílé objekty, černé pozadí).

Pruning

?Pruning

Pruning[,35]

Manipulate[Pruning[,n],{n,1,50,1,Appearance->"Labeled"}]

Thinning, Dilation, MorphologicalComponents

Thinning[]

Dilation[%,1]

Manipulate[Thinning[,n],{n,1,50,1,Appearance->"Labeled"}]

Počítání nohou stonožky:

skeleton = Thinning[]

prunedSkeleton=Pruning[skeleton,{12}]

subSkeleton=ImageSubtract[skeleton, prunedSkeleton]

Nalezení souvislých komponent:

components=MorphologicalComponents[subSkeleton]

Max[components]

ArrayPlot[components,ColorRules->{0->Black},ColorFunction-> "DarkBands"]

DeleteSmallComponents

?DeleteSmallComponents

Manipulate[DeleteSmallComponents[,n],{n,100,3000}]

Odstranění bílého a černého šumu pomocí DeleteSmallComponents:

ColorNegate[DeleteSmallComponents[ColorNegate[DeleteSmallComponents[,50]],50]]

Ostatní

Inpaint

Inpaint[, ]

Masku lze vyrobit např. pomocí nástroje Mask Tool v kontextové nabídce obrázku (pozor, není k dispozici v režimu SlideShow), k rozšíření tenkých čar poslouží Dilation.

im = ;

red=ColorSeparate[im][[1]]

bin=Binarize[red,0.93]

dilate=Dilation[bin,1]

Inpaint[im,dilate]

Clear[im,red,bin,dilate]

ImageCollage

?ImageCollage

buildings={,,,,,,,,,,,};

ImageCollage[buildings,Method->"Rows",ImagePadding->5]

europeFlagPopulation={3.238316`*^6->,85293.`->,8.441263`*^6->,9.49829`*^6->,1.0815599`*^7->,3.73588`*^6->,7.349462`*^6->,4.378806`*^6->,1.141453`*^6->,1.0589806`*^7->,5.611187`*^6->,1.338832`*^6->,49709.`->,5.418763`*^6->,6.3782963`*^7->,8.1804228`*^7->,29111.`->,1.1445691`*^7->,65605.`->,9.933795`*^6->,331996.`->,4.631391`*^6->,86159.`->,6.1087395`*^7->,95732.`->,1.847708`*^6->,2.22626`*^6->,37009.`->,3.278337`*^6->,530018.`->,2.069219`*^6->,420557.`->,3.495657`*^6->,30500.`->,633167.`->,1.6761552`*^7->,4.991997`*^6->,3.8331803`*^7->,1.0704534`*^7->,2.1338815`*^7->,32448.`->,9.83501`*^6->,5.488775`*^6->,2.044766`*^6->,4.7042602`*^7->,2495.`->,9.545823`*^6->,7.76182`*^6->,4.4696741`*^7->,6.3177406`*^7->,826.`->};

ImageCollage[europeFlagPopulation,Automatic,500,ImagePadding->3]

CurrentImage

CurrentImage[]

Filtrování vstupu z kamery v reálném čase:

Dynamic[ImageReflect[CurrentImage[],Bottom->Top]]

ImagePartition

Více informací: http://en.wikipedia.org/wiki/Fifteen_puzzle

ImagePartition[, 80]

%//TableForm

Manipulate[
Module[{pieces=Flatten[ImagePartition[images[[i]],80]]},
Grid[Map[GameBox[#1,Hold[$Game],pieces,hint]&,$Game,{2}],Spacings->{0,0},ItemSize->All]],
Row[{Control[{{i,1,""},Table[j->thumbs[[j]],{j,Length[images]}],ControlType->SetterBar}],Control[{{hint,False},{True,False}}]},Spacer[10]],
Row[{Control[{{dummy,"",""},Button["reshuffle",$Game=Partition[ShufflePuzzle[4,steps],4]]&}],Spacer[10],Control[{{steps,25,"easy"},1,100,1,ImageSize->Small,AppearanceElements->None}],Spacer[5],"hard"}],
{$Game,{Partition[ShufflePuzzle[4,100],4]},ControlType->None},
AutorunSequencing->{1,2},

Initialization:>(
(* This is a helper function to make your image fit for the puzzle *)puzzleImage[img_?ImageQ]:=Image[ImageResize[ImageCrop[img,{Min[ImageDimensions[img]]},AspectRatio->1],320],ImageSize->Automatic];
images={,,};thumbs=Thumbnail[#,40]&/@images;ShowPuzzlePiece[img_,n_,show_]:=If[show,Graphics[{Inset[img,Automatic,Automatic,2],Text[Style[n,"Label",12],{Left,Top},{Left,Top},Background->Opacity[.5,White]]},PlotRange->1,ImageSize->80],img];GameBox[n_,game_,img_,num_]/;n==Max[Release[game]]:=Button[If[GameSolvedQ[game],Pane[Style["Success!",Gray,15],Alignment->Center,ImageSize->{80,80}],""],Null,Enabled->False,FrameMargins->0,Appearance->"Frameless"];GameBox[n_,game_,img_,num_]:=Button[ShowPuzzlePiece[img[[n]],n,num],fff[game,{GameMove[n,game]}]/.{fff->Set,Hold->List},Enabled->!GameSolvedQ[game],FrameMargins->0,Appearance->"Frameless"];
GameSolvedQ[game_]:=OrderedQ[Flatten[Release[game]]];AdjacentQ[n_,game_]:=(Sort[Abs[Flatten[Position[game,n]-Position[game,Max[game]]]]]==={0,1});
GameMove[n_,game_]:=If[AdjacentQ[n,Release[game]],Release[game]/.{n->Max[Release[game]],Max[Release[game]]->n},Identity[Release[game]]];
ShufflePuzzle[size_,steps_]:=Module[{list=Range[(size^2)],pos=(size^2),target,cstep=1},Do[target=RandomChoice[Cases[{pos-1,pos+1,pos-size,pos+size},x_/;1≤x≤(size^2)]];list=ReplacePart[list,{pos->list[[target]],target->list[[pos]]}];pos=target,{steps}];list];)]

THIS NOTEBOOK IS THE SOURCE CODE FROM
"Picture Puzzle" from The Wolfram Demonstrations Project
 http://demonstrations.wolfram.com/PicturePuzzle/
Contributed by: Yu-Sung Chang
Based on a program by: Jon McLoone

Cvičení

Odstraňte letící ptáky z následující fotografie:
 
	

Návod. Použijte Inpaint s vhodnou maskou, kterou vyrobíte např. pomocí nástroje Mask Tool. Nejste-li spokojeni se vzhledem retušovaného obrázku, zkuste volbu Method->{"TextureSynthesis","MaxSamples"->N}, kde N je dostatečně velké číslo.

Najděte vhodnou projektivní transformaci, která „narovná“ následující snímek Westminsterského opatství. Výsledkem by měl být obrázek, u kterého bude mít divák dojem, že jeho pohled směřuje kolmo k průčelí budovy.

	

Návod. Použijte funkci FindGeometricTransform, která pro zadané dvě sady bodů dokáže najít projektivní transformaci zobrazující jednu sadu na druhou. V obrázku najděte souřadnice 4 vhodných bodů a poté předepište souřadnice jejich obrazů. (Využijte např. toho, že okraje věží se musejí zobrazit na svislé úsečky). Pozor, argumenty FindGeometricTransform se zapisují v opačném pořadí, než by bylo přirozené (nejprve souřadnice v cílovém obrázku, poté souřadnice ve zdrojovém obrázku). Jakmile najdete vhodnou transformaci, použijte funkci ImagePerspectiveTransformation. Může se vám hodit volba DataRange->Full, pomocí PlotRange nastavte vhodný výřez tak, aby nový obrázek měl opět tvar obdélníku.