Automatinis nuotraukų mažinimas

app-resizeme-512

Prisireikė darbe sumažinti krūvą kamerų nuotraukų. Nuotraukų, švelniai tariant, daug. Labai daug, apie 6 mln. vnt. (~1 TB).  Visos nuotraukos tvarkingai sudėliotos į hierarchinę aplankų struktūrą pagal kameros pavadinimą, metus, mėnesį ir dieną, todėl „prieiti” iki konkrečios nuotraukos nėra labai jau paprasta. Dar įdomiau, kad ne visų kamerų vaizdus reikia mažinti. Kai kurie kamerų vaizdai jau yra reikiamo dydžio, ir juos papildomai apdorojant būtų be reikalo gaištamas laikas. Ką daryti..? Aš radau tris skirtingo sudėtingumo šios užduoties įgyvendinimo variantus. 

Image Resizer

ContextMenu
ImageResizer

Tai pats paprasčiausias variantas. Atsisiuntus ir susiinstaliavus „Image Resizer”, jums beliks tik pasirinkti norimus sumažinti paveikslėlius, ant jų paspausti dešiniu pelės klavišu, išsirinkti meniu punktą „Rezise pictures”, nustatyti norimus parametrus ir paspausti „Resize”. Viską kitą programa padarys pati.

Didžiausias programos privalumas yra tas, kad ji turi intuityvią naudotoją sąsają. Taip pat yra pakankamai protinga ir gali tik mažinti paveikslėlius (uždėjus varnelę „Only shrink pictures”). Kitos programos, tuo tarpu, bando ištampyti mažus paveikslėlius iki nustatyto dydžio. Labai patogu, kad „Image Resizer” gali ne tik perrašyti failų originalus bet ir sukurti sumažintų paveikslų kopiją.

Didžiausiais programos trūkumas – „Image Resizer” negalima „užsiundyti” ant visos nuotraukų direktorijos. T. y. reikia rankiniu būdu pasirinkti konkrečius nuotraukų failus. Mano atveju, ši programa nelabai tinka, nes kaip minėjau įžangoje, mano nuotraukos tvarkingai sudėliotos į hierarchinę aplankų struktūrą pagal kameros pavadinimą, metus, mėnesį ir dieną, todėl „prieiti” iki konkrečios nuotraukos nėra labai paprasta ir užima per daug laiko. Taip pat programa kaip ir neturi jokio vizualinio darbo eigos indikatoriaus. T. y. paleidus „Resize”, apie vykstantį darbą galima suprasti tik iš failų dydžių ir iš CPU apkrovos. Nėra nurodoma, ką programa konkrečiai veikia.

Image Magick

Imagemagick-logoImage Magick – tai, tikriausiai, pats galingiausiais ir universaliausiais įrankis, skirtas manipuliacijos su paveikslėliais iš komandinės eilutės. Su „Image  Magick” galime keisti paveikslėlio dydį ir formatą, transformuoti paveikslėlį (pasukti, pakreipti ir pan.), kai kurias paveikslėlio vietas padaryt permatomas, ant paveikslėlio užpiešti geometrines figūras, sukomponuoti kelis paveikslus į vieną ar pakeisti paveikslėlio spalvinę gamą. Ir visa tai (tikrai dar daugiau visko) galima atlikti iš komandinės eilutės! Na bet kol kas mus domina tik paveikslėlių mažinimas.

Apie paveikslėlių mažinimą yra parašyta išsami pasakėčia,  kurią tikrai vertėtų kada nors pasiskaityti, tačiau pradžiai užteks ir tokios komandos:

convert image.jpg -resize 640x480> image_min.jpg

Ši komanda nurodytą paveikslėlį image.jpg sumažins iki nurodyto dydžio (atsižvelgdama į originalaus paveikslėlio proporcijas) ir išsaugos nauju vardu image_min.jpg. Norint, kad komanda originalų paveikslėlį perrašytų, tiesiog reikia nurodyti tokį pat naujo failo pavadinimą kaip ir originalaus failo. Easy!

O kaip tokią komandą „užsiundyti” ant visų pageidaujamų failų? T. y. kaip su „Image Magick” sumažinti visas aplanke esančias nuotraukas? O gi, pasirodo, labai paprastai. Mums padės komandinės eilutės komanda FOR, kuri, kaip ir reikia suprasti, užsuks ciklą, kuris pereis per visus failus ir jiems įvykdys dydžio sumažinimo komandą:

FOR /R %f IN (*.jpg) DO convert "%f" -resize "640x480>" "%f"

Nuotraukų mažinimu aš iš principo siekiau išpešti kuo daugiau laisvos duomenų saugyklos vietos, todėl man labiau rūpėjo sumažinti ne tiek matmenimis, kiek užimama disko vieta dideles nuotraukas. Taip gimė komanda, kuri mažina tik nuotraukas didesnes nei 100 kB:

for /r %f in (*) do if %~zf gtr 100000 convert "%f" -resize "640x480>" "%f"

Aišku, didelių nuotraukų dydžio galima ir nekeisti. Kartais gali užtekti tik sumažinti nuotraukos kokybę. Po ilgų išieškojimų, pagaminau tokią komandą:

FOR /R %f IN (*.jpg) DO convert "%f" -strip -interlace Plane -gaussian-blur 0.05 -quality 75% "%f"

Norint, kad visas sumažintos kokybės nuotraukas komanda išsaugotų šakniniame kataloge, komandą galima pakeisti taip:

FOR /R %f IN (*.jpg) DO convert "%f" -strip -interlace Plane -gaussian-blur 0.05 -quality 75% "%~nxf"

Prierašas %~nxf  gražina tik failo pavadinimą, tuo tarpu kaip %f gražina visą kelią iki failo.

Image Magick + VBS

kameros_struktura

Prieš tai aprašytas metodas puikiai tinka absoliučiai visų direktorijoje ir šios subdirektorijose esančių nuotraukų sumažinimui. Tačiau ką daryti, jeigu norime sumažinti tik kai kurių kamerų nuotraukas? Tarkim, kad norime sumažinti tik „Kamera2” ir „Kamera4” vaizdus (žr. paveikslą viršuje), visus kitus paliekant ramybėje. Bandant panaudoti prieš tai aprašytą for /R metodą tektų komandą vykdyti du kartus: vieną kartą direktorijai \Kameros\Kamera2, kitą kartą – \Kameros\Kamera4. Tai gan nepatogu bei sunku automatizuoti.

Kiek pasigilinus ir „pasigooglinus” gimė štai toks Kameru_vaizdu_suspaudimas.vbs skriptas:

Const srcDir = "C:"
Const camerasDir = "\Kameros"

Const minFileSize = 100 'kb

Set fso = CreateObject("Scripting.FileSystemObject")
set img = CreateObject("ImageMagickObject.MagickImage.1")

' Skriptui aktualiu kameru masyvas:
Dim kameros
kameros = Array("Kamera2","Kamera4")

Sub ShowSubFolders(Folder)
  For Each Subfolder In Folder.SubFolders
	set objShell = WScript.CreateObject ("WScript.Shell")	
	objShell.CurrentDirectory = fso.GetFolder(Subfolder)
	WScript.Echo (objShell.CurrentDirectory)
    Set files = SubFolder.Files
    For Each file In files
		If round(file.Size / 1024,1) >= minFileSize Then
			WScript.Echo Subfolder & "\" & file.Name & " " &  round(file.Size/1024,1) & "kb"
			img.Convert file.name, "-resize", "640x480>", file.name
		Else
			WScript.Echo "SKIP " & Subfolder & "\" & file.Name & " " &  round(file.Size/1024,1) & "kb" 
		End If
    Next
    ShowSubFolders Subfolder
  Next
End Sub



WScript.Echo "-------------------------------------------"
WScript.Echo "Vaizdo kameru vaizdu suspaustimo skriptas"

DateInfo = DateInfo & Now & VbCrLf
Wscript.Echo DateInfo

WScript.Echo "-------------------------------------------"

For Each x In kameros
	
	If fso.FolderExists(srcDir & camerasDir  & "\" & x ) Then
	
		Set kamera = fso.GetFolder(srcDir & camerasDir  & "\" & x )
		WScript.Echo "Nulindau i " & kamera		
		Set folderObj = fso.GetFolder(kamera) 		
		set objShell = WScript.CreateObject ("WScript.Shell")	
		objShell.CurrentDirectory = fso.GetFolder(kamera)
		WScript.Echo (objShell.CurrentDirectory)
			
		set files = folderObj.Files
		for each file in files
			If round(file.Size / 1024,1) >= minFileSize Then
				WScript.Echo kamera & "\" & file.name & " " &  round(file.Size/1024,1) & "kb"
				img.Convert file.name, "-resize", "640x480>", file.name
			Else
				WScript.Echo "SKIP" & kamera & "\" & file.name & " " &  round(file.Size/1024,1) & "kb"
			End If
		Next	
		
		'Pereisim per visus subfolderius:
		ShowSubFolders fso.GetFolder(kamera)
		
	Else
		Wscript.Echo "Folder """ & srcDir & camerasDir  & "\" & x & """ does not exist?"
	End If
	
Next

Skriptas gavosi pakankamai gudrus. Jis iš nurodytos direktorijos „C:\Kameros\” atsirenka tik masyve „kameros” nurodytas subdirektorijas „Kamera2” ir „Kamera4”.  Tada paeiliui pereina per šių subdirektorijų subdirektorijas  ir suranda visus jose esančius failus. Jeigu konkretus failas yra didesnis nei nurodytas minFileSize = 100 KB, jam įvykdo nuotraukos sumažinimo komandą. Jei failas mažesnis – jį praleidžia. Visą savo darbo eigą skirptas išspausdina į komandinę eilutę.

Iš komandinės eilutės  šį skriptą geriausia paleisti taip:

cscript.exe //nologo //D C:\Kameru_vaizdu_suspaudimas.vbs

Tai apsaugos nuo iššokančių Windows Script Host pranešimų ir kitokių  negandų. Geriausia, apskritai pastarąją eilutę įrašyti į BAT failiuką. Taip skriptą galima bus galima paleisti Windows Schedule priemonėmis.

Paruoštą naudoti VBS skriptą su paleidimo BAT failu galite atsisiųsti iš čia: Kameru_vaizdu_suspaudimas.zip

P. S. patarimas dėl lietuviškų rašmenų. Bent jau aš susidūriau su tokia problema, kad vbs skriptas piktybiškas nenorėjo  pripažinti lietuviškų raidžių kamerų pavadinimuose. Pav. C:\Kameros\Obuolys puikiausiai veikė, o vat C:\Kameros\Kriaušė niekaip. Šiokį tokį sprendimą radau StackOverflow forumuose. Čia protingas žmogėnas pasiūlė naudoti ne pilną katalogo/failo pavadinimą, o jo „ShortPath” versiją. „ShortPath” vardus galima sužinoti su komanda dir /x:

shortpathGrįžtant prie mano VBS skripto, „ShortPath” vardų panaudojimas realizuojamas pakeišiant kamerų direktorijų pavadinimų masyvo reikšmes iš:

kameros = Array("Obuolys","Kriaušė")

į:

kameros = Array("Obuolys","KRIAU~1")

Štai ir viskas!
doctorwho