Digitální zpracování obrazu

Antonín Slavík
MFF 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_2016_17/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

Změna velikosti

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

(Salvador Dalí, Gala nude looking at the sea which at 18 metres appears as president Lincoln, 1975)

{w,h}=ImageDimensions[gala]

?ImageResize

ImageResize[gala, 100]

ImageResize[gala,{200,100}]

ImageResize[gala, Scaled[1/2]]

Získání výřezu

?ImageTake

ImageTake[gala,{160,489},{140,240}] (* řádky 160 až 489, sloupce 140 až 240 *)

ImageTake[gala,{1,h,2},{1,w}] (* Každý druhý řádek *)

Doplnění okrajů

?ImagePad

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

Rotace, osová symetrie

?ImageRotate

Manipulate[ImageRotate[lena,α Degree],{α,0,360}]

?ImageReflect

ImageReflect[lena, Left-> Right]

Obecné (nelineární) transformace

?ImageTransformation

?ImageForwardTransformation

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 souřadnice 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]

Rybí oko:

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

lena=;

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

Spirálový efekt:

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

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"}]

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.

Mozaikový efekt:

Manipulate[ImageTransformation[lena,n Floor[#/n]+1&,DataRange->Full],{n,1,10,1}]

Perspektivní transformace

?ImagePerspectiveTransformation

Perspektivní transformace roviny, která je vzhledem k homogenním souřadnicím popsána zadanou maticí. Vzhledem ke kartézským souřadnicím v (ℝ^2) jde o následující transformaci:

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}]

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

Remove the perspective distortion of the road:

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

Ukázka z Wolfram Blogu (Andrew Moylan: The Ambassadors):

img=Import["http://www.karlin.mff.cuni.cz/~slavik/pokrocili_ls_2016_17/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

?ImageAdjust

balony=;

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"}]

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"}]

Sloučení obrázků

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]]

Aritmetické operace

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

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

Manipulate[GradientFilter[brno1,r]//ImageAdjust,{r,1,10,1}]

Zaostření (zvýraznění hran)

Manipulate[Sharpen[brno1,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}]]}]

fructose=; (* source: ChemicalData["Fructose","CHColorStructureDiagram"] *)

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

caffeine=; (* source: ChemicalData["Caffeine","CHColorStructureDiagram"] *)

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

Clear[fructose,caffeine]

Konvoluce a dekonvoluce

Každé lineární zobrazení, které je invariantní vůči posunutí, lze popsat jako konvoluci obrázku s jistou maticí.

ImageConvolve[brno1,  (({{1, 1, 1}, {1, 1, 1}, {1, 1, 1}}))/9]

ImageDeconvolve[%,  (({{1, 1, 1}, {1, 1, 1}, {1, 1, 1}}))/9]

ImageDeconvolve[ , BoxMatrix[3]/49]

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

?MeanShiftFilter

MeanShiftFilter[,3,.1,MaxIterations->20]

?KuwaharaFilter

Manipulate[KuwaharaFilter[lena,r],{r,1,10,1}]

?BilateralFilter

BilateralFilter replaces each pixel by a weighted average of its neighbors, using normalized Gaussian matrices as weights.

BilateralFilter[,2,.2]

?CurvatureFlowFilter

Curvature flow filtering is an anisotropic diffusion method used for smoothing images while preserving edges. It effectively spreads the curvature along a contour, thereby rounding corners and reducing the Euclidean length of contours.

CurvatureFlowFilter[,2]

Registrace snímků

ImageAlign

?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]

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

matches=ImageCorrespondingPoints[i1,i2];
MapThread[Show[#1,Graphics[{Red,MapIndexed[Inset[Style[#2[[1]],Bold],#1]& ,#2]}]]&,{{i1,i2},matches}]

Morfologické operace

Pruning

?Pruning

Pruning[,35]

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

Thinning, MorphologicalEulerNumber

?Thinning

Thinning[]

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

Count the legs of a centipede:

skeleton = Thinning[]

prunedSkeleton=Pruning[skeleton,{12}]

subSkeleton=ImageSubtract[skeleton, prunedSkeleton]

MorphologicalEulerNumber[subSkeleton]

components=MorphologicalComponents[subSkeleton]

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

Find the loops of a graph:

Pruning[Thinning[], Infinity]

DeleteSmallComponents

?DeleteSmallComponents

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

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

Ostatní

Inpaint

?Inpaint

Inpaint[, ]

Inpaint takes a Method option that specifies the inpainting method to be used. By default the "Diffusion" method is used. Possible settings are:

({{      , "Diffusion", iterative isotropic diffusion method}, {      , "TotalVariation", iterative total variation method}, {      , "FastMarching", fast marching method}, {      , "NavierStokes", Navier–Stokes method}})

Masku lze vyrobit pomocí nástroje Mask Tool v kontextové nabídce obrázku.

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]

TextRecognize,BarcodeRecognize,BarcodeImage

?TextRecognize

scan=;

TextRecognize[scan]

ImageDimensions[scan]

ImageResize[scan,Scaled[1/2]]

TextRecognize[%]

TextRecognize[]

TextRecognize[]

TextRecognize[]

TextRecognize[]

TextRecognize by default assumes that text to be recognized is in English. When working with other languages, better results may be obtained by setting the Language option to one of the following: “French”, “German”, “Italian”, “Portuguese”, “Russian”, “Spanish”.

TextRecognize[,Language->"Russian"]

?BarcodeRecognize

barcode=;

BarcodeRecognize[barcode]

qrcode=;

BarcodeRecognize[qrcode]

?BarcodeImage

BarcodeImage["http://www.mff.cuni.cz","QR"]

BarcodeRecognize[%]

CurrentImage

?CurrentImage

Table[ Pause[1];CurrentImage[], {5}]

Dynamic capturing and processing:

Dynamic[GradientFilter[CurrentImage[],2]]

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