Convert all .CR2 (camera raw) files to .TIF files in a directory ?

Hi, I’m trying to write a script where I can give the script a folder and it will look through that folder and all subfolders within for any .CR2 files and will use “Image Events” to save a copy of that file as a .TIF file.

So far, I’ve got a basic script that will save a copy of the file with the correct name, extension, and properties, but I don’t know how to make it able to drop a directory on it and have the script look through that folder and all subfolders for the appropriate files.

This is what I have so far:


set myFile to (choose file of type "CR2" without invisibles) as text
set trimmedName to (text 1 thru ((offset of "." in myFile) - 1) of myFile) & ".TIF"
set the targetPath to the (trimmedName) as Unicode text
try
	tell application "Image Events"
		launch
		set thisImage to open myFile
		save thisImage as TIFF in targetPath with icon
		close thisImage
	end tell
on error errorMessage
	display dialog errorMessage buttons {"Cancel"} default button 1
end try

Can anyone help point me in the right direction?
Thanks!

You may achieve your goal with:

# Entry point used if you double click the script icon
on run
	set aFold to choose folder
	my main(aFold)
end run

# Entry point used if you drop a folder on the script icon
on open droppedItem
	set aFold to item 1 of droppedItem
	my main(aFold)
end open

on main(aFold)
	tell application "System Events"
		set theCR2 to path of every disk item of aFold whose name extension is "CR2"
	end tell
	
	repeat with myFile in theCR2
		set trimmedName to (text 1 thru ((offset of "." in myFile) - 1) of myFile) & ".TIF"
		set targetPath to trimmedName as Unicode text
		try
			tell application "Image Events"
				launch
				set thisImage to open myFile
				save thisImage as TIFF in targetPath with icon
				close thisImage
			end tell
		on error errorMessage
			display dialog errorMessage buttons {"Cancel"} default button 1
		end try
	end repeat
end main

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 2 aout 2019 10:35:16

This is a solution which recursively processes files and folders including subfolders.

Save the script as application. You can choose folders on launch and you can drop files and folders onto the icon


on run
	set chosenFolders to (choose folder with multiple selections allowed)
	processItems(chosenFolders)
end run

on open droppedItems
	processItems(droppedItems)
end open

on processItems(theItems)
	repeat with oneItem in theItems
		tell application "System Events"
			set oneDiskItem to disk item (oneItem as text)
			set {class:itemClass, name extension:fileExtension, path:itemPath} to oneDiskItem
			set isFolder to itemClass is folder
		end tell
		if isFolder then
			tell application "System Events" to set subItems to path of disk items of oneDiskItem
			processItems(subItems)
		else
			if fileExtension is "CR2" then
				set targetPath to (text 1 thru -4 of itemPath) & "TIF"
				try
					tell application "Image Events"
						launch
						set thisImage to open (itemPath as alias)
						save thisImage as TIFF in targetPath with icon
						close thisImage
					end tell
				on error errorMessage
					display dialog errorMessage buttons {"Continue", "Cancel"} default button 1
				end try
			end if
		end if
	end repeat
end processItems

or with AppKit’s NSImage instead of Image Events which converts the images without opening them


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on run
	set chosenFolders to (choose folder with multiple selections allowed)
	processItems(chosenFolders)
end run

on open droppedItems
	processItems(droppedItems)
end open

on processItems(theItems)
	repeat with oneItem in theItems
		tell application "System Events"
			set oneDiskItem to disk item (oneItem as text)
			set {class:itemClass, name extension:fileExtension, POSIX path:itemPath} to oneDiskItem
			set isFolder to itemClass is folder
		end tell
		if isFolder then
			tell application "System Events" to set subItems to path of disk items of oneDiskItem
			processItems(subItems)
		else
			if fileExtension is "CR2" then
				set targetPath to (text 1 thru -4 of itemPath) & "TIF"
				set theImage to (current application's NSImage's alloc()'s initWithContentsOfFile:itemPath)
				set tiffImageData to theImage's TIFFRepresentation()
				(tiffImageData's writeToFile:targetPath atomically:false)
			end if
		end if
	end repeat
end processItems

If you want to treat also subfolders you may use:

----------------------------------------------------------------
use AppleScript version "2.5"
use framework "Foundation"
use script "FileManagerLib" version "2.2.2"
use scripting additions
----------------------------------------------------------------

# Entry point used if you double click the script icon
on run
	set aFold to choose folder
	my main(aFold)
end run

# Entry point used if you drop a folder on the script icon
on open droppedItem
	set aFold to item 1 of droppedItem
	my main(aFold)
end open

on main(aFold)
	set posixPath to POSIX path of aFold # Oops, was wrongly targetDir
	set theFiles to objects of posixPath result type paths list with searching subfolders without include folders and include invisible items
	
	repeat with myFile in theFiles
		if myFile ends with ".CR2" then
			set trimmedName to (text 1 thru ((offset of "." in myFile) - 1) of myFile) & ".TIF"
			set targetPath to trimmedName as Unicode text
			try
				tell application "Image Events"
					launch
					set thisImage to open myFile
					save thisImage as TIFF in targetPath with icon
					close thisImage
				end tell
			on error errorMessage
				display dialog errorMessage buttons {"Cancel"} default button 1
			end try
		end if
	end repeat
end main

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 2 août 2019 15:40:05

I’ve included a revised version of my script below. If saved as an application, and if the number of files to be processed exceeds the file-count specified, the script displays a progress bar.


--Set default values.
set sourceExtension to "CR2"
set destinationExtension to "TIFF"
set dialogThreshold to 5

--Prompt for source folder.
choose folder default location (path to home folder)
set sourceFolder to quoted form of (text 1 thru -2 of POSIX path of result)

--Create list of source files.
do shell script "find " & sourceFolder & " -type f -iname *." & sourceExtension & "; true"
set sourceFiles to paragraphs of result

--Display dialogs if file count exceeds the dialog threshold value.
set fileCount to (count sourceFiles)
if fileCount > 5 then
	set dialogText to "Convert " & fileCount & " " & sourceExtension & ¬
		" files to " & destinationExtension & " forrmat."
	display dialog dialogText buttons {"Cancel", "OK"} default button 2 cancel button 1 ¬
		with title sourceExtension & " to " & destinationExtension & " converter."
	set showProgress to true
else
	set showProgress to false
end if

--Initialize progress dialog.
if showProgress then
	set progress description to "File being processed"
	set progress total steps to fileCount
end if

--Loop through souorce files.
repeat with i from 1 to fileCount
	set aFile to item i of sourceFiles
	
	--Show progress dialog.
	if showProgress then
		set progress additional description to i & " of " & fileCount
		set progress completed steps to i
	end if
	
	--Create destination files.
	set destinationFile to text 1 thru -4 of aFile & destinationExtension
	try
		do shell script "sips --setProperty format " & destinationExtension & ¬
			" " & (quoted form of aFile) & " --out " & quoted form of destinationFile
	on error errorMessage
		set dialogText to "The SIPS utility reported an error:" & return & return & errorMessage
		display dialog dialogText buttons {"Cancel", "Continue"} default button 2 cancel button 1 ¬
			with title sourceExtension & " to " & destinationExtension & " converter." with icon stop
	end try
end repeat

We may ask the system to do the conversion using ASObjC.

use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use script "FileManagerLib" version "2.2.2"

# Entry point used if you double click the script icon
on run
	set aFold to choose folder
	my main(aFold)
end run

# Entry point used if you drop a folder on the script icon
on open droppedItem
	set aFold to item 1 of droppedItem
	my main(aFold)
end open

on main(aFold)
	set posixPath to POSIX path of aFold
	set theFiles to objects of posixPath result type paths list with searching subfolders without include folders and include invisible items
	
	repeat with myFile in theFiles
		if myFile ends with ".CR2" then
			set trimmedName to (text 1 thru ((offset of "." in myFile) - 1) of myFile) & ".TIF"
			set targetPath to trimmedName as Unicode text
			
			set theImage to (current application's NSImage's alloc()'s initWithContentsOfFile:(myFile))
			set theData to theImage's TIFFRepresentation()
			set newRep to (current application's NSBitmapImageRep's imageRepWithData:theData)
			
			set newData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompressionNone:1})
			
			set aRes to (newData's writeToFile:targetPath atomically:true) as boolean
		end if
	end repeat
end main

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 2 aout 2019 19:35:25

I created a folder with a bunch of subfolders all with multiple CR2 files and then timed a number of the scripts in this thread. For testing purposes, the results were close enough as to be equal:

StefanK (first script) - 33 seconds
StefanK (second script) - 27
peavine - 27
Yvan (last script using ASObjC) - 32

The OP has a lot of great choices.

FWIW, I did a couple of tests yesterday with some fairly large files, using ASObjC code similar to that posted here. Although it seemed like a good idea, AppleScript’s memory management with ASObjC is pretty hopeless. Normally that doesn’t matter in the scheme of things, especially if you’re talking applets, but in this case the result was gigabytes of memory being consumed, and at some point slow-downs/stalls from attempts to compress memory.

So for once I’d advise against doing the conversions themselves in ASObjC.

Edit: I now realise I had a deeper folder of .cr2 files that were also being processed in my test, so there were more than 60 files altogether being processed — more than I thought. The point still stands, though.

Hello to all. I will not test, but I think that the AppleScriptObjc variant provided above by StefanK is better. It uses all the advantages of “System Events” and all the advantages of NSImage. It can be somewhat improved by removing the sending of some events from the repeat loop:

  1. Bring tell application “System Events” out of the repeat loop
  2. Create 2 ready links in the form of properties:
property NSImage: a reference to NSImage of current application
property NSBitmapImageRep: a reference to NSBitmapImageRep of current application
  1. You can not create an isFolder variable and check immediately:
if itemClass is folder then

I disagree.

Why? A reference to System Events is only used in lines containing SE terminology

The NSBitmapImageRep conversion is redundant anyway and it’s not useful to create a property if the symbol is only used once.

No you can’t. folder is part of System Events. Though the script compiles it throws a runtime error.

Shane Stanley warned me that I was asking my script to do useless job.
Here is the cleaned version.

use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use script "FileManagerLib" version "2.2.2"

# Entry point used if you double click the script icon
on run
	set aFold to choose folder
	my main(aFold)
end run

# Entry point used if you drop a folder on the script icon
on open droppedItem
	set aFold to item 1 of droppedItem
	my main(aFold)
end open

on main(aFold)
	set posixPath to POSIX path of aFold
	set theFiles to objects of posixPath result type paths list with searching subfolders without include folders and include invisible items
	
	repeat with myFile in theFiles
		if myFile ends with ".CR2" then
			set trimmedName to (text 1 thru ((offset of "." in myFile) - 1) of myFile) & ".TIF"
			set targetPath to trimmedName as Unicode text
			
			set theImage to (current application's NSImage's alloc()'s initWithContentsOfFile:(myFile))
			set theData to theImage's TIFFRepresentation()
			
			set aRes to (theData's writeToFile:targetPath atomically:true) as boolean
		end if
	end repeat
end main

Now I just wait for the timing got by peavine.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 3 aout 2019 09:40:14

Where can I download cr2 photos for free? I would like to test the following code (modified from StefanK) and compare it with other variants. I would like to have only the best in my library:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
property NSImage : a reference to NSImage of current application

on run
	set chosenFolders to (choose folder with multiple selections allowed)
	my processItems(chosenFolders)
end run

on open droppedItems
	my processItems(droppedItems)
end open

on processItems(theItems)
	tell application "System Events"
		repeat with oneItem in theItems
			set oneDiskItem to disk item (oneItem as text)
			set {class:itemClass, name extension:fileExtension, POSIX path:itemPath} to oneDiskItem
			
			if itemClass is folder then
				set subItems to path of disk items of oneDiskItem
				my  processItems(subItems)
			else
				if fileExtension is "CR2" then
					set targetPath to (text 1 thru -4 of itemPath) & "TIF"
					set theImage to (NSImage's alloc()'s initWithContentsOfFile:itemPath)
					set tiffImageData to theImage's TIFFRepresentation()
					(tiffImageData's writeToFile:targetPath atomically:false)
				end if
			end if
		end repeat
	end tell
end processItems

You may test with an other kind of picture files, PNG for instance
You just need to edit one instruction:
replace
if fileExtension is “CR2” then
by
if fileExtension is “PNG” then

I got my sample files with a free version of a DXO application.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 3 août 2019 11:22:40

Was full processed 10 .cr2 files. My machine may be more slower, but we should see all in comparsion:

  1. KniazidisR (modified from StefanK ObjC script) - 1 min 34 sec
  2. Yvan Koenig (last script. Tested with “FileManagerLib” version “2.2.1”. I don’t have “2.2.2” yet)- 1 min 52 sec
  3. StefanK ObjC script - 2 min 47 sec
  4. peavine - for 10 .cr2 files - 1 min 02 sec, 11 .cr2 files - 1 min 07 sec

Thanks to all. And thank you, peavine. Your code goes to my library.

PS: It would be interesting for someone to test with a larger number of files (> 100) and with a complex file structure. For this need a more powerful computer than mine. One thing is clear for me now - the sips utility converts and writes an image faster than ObjC. I don’t know if ObjC code can be made better in this case.

UPDATE

I had an idea how to improve the code AppleSriptObjC radically. Soon I will give it.

On my side I don’t use the shell because many times it’s not at ease with file paths embedding non ASCII characters.
Quite often I treat files grabbed from Apple Music and some of them contain characters stored in composite form. They aren’t appreciated by the shell.
Just a sample:
filename: 15-01 La Chanson Du Mal Aimé.m4p

I stored this string in a text file named beurk.txt on the destop and ran this script:

set aFile to (((path to desktop as text) as text) & "beurk.txt") as «class furl»
set theChars to characters of (read aFile) 

set aList to {}
repeat with aChar in theChars
	set end of aList to aChar & tab & id of aChar
end repeat
aList

the result is :

{“1 49”, “5 53”, “- 45”, “0 48”, “1 49”, " 32", “L 76”, “a 97”, " 32", “C 67”, “h 104”, “a 97”, “n 110”, “s 115”, “o 111”, “n 110”, " 32", “D 68”, “u 117”, " 32", “M 77”, “a 97”, “l 108”, " 32", “A 65”, “i 105”, “m 109”, “e 101”, “Ã 195”, “Å 197”, “. 46”, “m 109”, “4 52”, “p 112”, "
10"}

Don’t ask me why Apple Music is encoding the names this way.
I just have to take care of that.

You may build a folder containing numerous CR2 files using the copy function available in Finder.

Starting from a folder containing a subfolder :

1 - Microcontrast Auto.cr2
5 - Smart Lighting Uniform.cr2

I got
1 - Microcontrast Auto copie 2.cr2
1 - Microcontrast Auto copie 3.cr2
1 - Microcontrast Auto copie 4.cr2
1 - Microcontrast Auto copie.cr2
1 - Microcontrast Auto.cr2
5 - Smart Lighting Uniform copie 2.cr2
5 - Smart Lighting Uniform copie 3.cr2
5 - Smart Lighting Uniform copie 4.cr2
5 - Smart Lighting Uniform copie.cr2
5 - Smart Lighting Uniform.cr2

Of course you may replicate more times.

Then we may replicate the subfolder.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 3 août 2019 15:10:00

Yes, this is a reason why I search ObjC variant as fast as shell variant. So, I found it. The following code processes the same 11 files in 1 min 09 sec:


use AppleScript version "2.5" -- macOS 10.11 or later
use scripting additions
use framework "AppKit"
use script "Metadata Lib" version "2.0.1"
property NSImage : a reference to NSImage of current application

on run
	set chosenFolders to POSIX path of (choose folder with prompt "CHOOSE FOLDER WITH CR2 IMAGES" with multiple selections allowed)
	my processItems(chosenFolders)
end run

on open droppedItems
	my processItems(droppedItems)
end open

on processItems(chosenFolders)
	set thePaths to perform search in folders {chosenFolders} predicate string "kMDItemFSName ENDSWITH[c] '.cr2'"
	repeat with oneItem in thePaths
		set targetPath to (text 1 thru -4 of oneItem) & "TIF"
		set theImage to (NSImage's alloc()'s initWithContentsOfFile:oneItem)
		set tiffImageData to theImage's TIFFRepresentation()
		(tiffImageData's writeToFile:targetPath atomically:false)
	end repeat
end processItems

I have 4 Gb RAM and 2-core processor. Tested without an external disk using Script Debugger.

I ran Yvan’s original and revised ASObjC scripts on my test folder (twice each). I also reran my script just to make sure nothing was amiss. The timings in seconds are:

Peavine: - 27
Yvan (original) - 31 and 31
Yvan (revised) - 27 and 26

Just by way of background, the test folder contained a total of 18 CR2 files, located 3 folders deep, with 6 CR2 files in each folder. The CR2 files contained approximately 25MB each and the created TIFF files contained about 110MB.

My computer is a 2018 Mac mini with 16GB of memory and a 6-core I5 CPU. The test folder was on an external USB3 SSD, which has read and write speeds of about 400MB/s. I reran a few scripts with the test folder on the computer’s internal SSD and the times were about 10 percent faster, as expected.

Before a timing run, I recompiled and saved the script. The code used to time the scrpts was:

[initial dialogs if any]
set startTime to (time of (current date))
[script excluding initial dialogs}
set executeTime to (time of (current date)) - startTime

Edit: the external drive that contains the test folder is not indexed by spotlight.

@KniazidisR

Your script fails if you select several folders in choose folders.

error “Il est impossible d’obtenir POSIX path of {alias "SSD 500:Users::desktop:dossier test:", alias "SSD 500:Users::desktop:DxO OpticsPro 11 Samples:"}.” number -1728 from POSIX path of {alias “SSD 500:Users::desktop:dossier test:", alias "SSD 500:Users::desktop:DxO OpticsPro 11 Samples:”}

Happily, perform search doesn’t require POSIX paths.

Here is a cleaned version:


use AppleScript version "2.5" -- macOS 10.11 or later
use scripting additions
use framework "AppKit"
use script "Metadata Lib" version "2.0.1"

property NSImage : a reference to NSImage of current application

on run
	set chosenFolders to choose folder with prompt "CHOOSE FOLDER WITH CR2 IMAGES" with multiple selections allowed # REMOVED POSIX path of
	# Here chosenFolders is always a list of aliases
	my processItems(chosenFolders)
end run

on open droppedItems
	# Here droppedItems is always a list of aliases
	my processItems(droppedItems)
end open

on processItems(chosenFolders)
	# Here chosenFolders is always a list of aliases
	set thePaths to perform search in folders chosenFolders predicate string "kMDItemFSName ENDSWITH[c] '.cr2'" # REMOVED the {} which enclosed chosenfolders
	repeat with oneItem in thePaths
		set targetPath to (text 1 thru -4 of oneItem) & "TIF"
		set theImage to (NSImage's alloc()'s initWithContentsOfFile:oneItem)
		set tiffImageData to theImage's TIFFRepresentation()
		(tiffImageData's writeToFile:targetPath atomically:false)
	end repeat
end processItems

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 3 août 2019 16:49:20

Ok, thanks for that. How about the speed?