Extracting FujiFilm Simulation Data

Here is a short write-up on a script I wrote to extract Fujifilm custom recipe data from EXIF metadata and make it accessible to Apple Photos and Adobe Lightroom.

Extracting FujiFilm Simulation Data

So, I'm getting around to blinging out my camera—it is summer and vacation time is right around the corner. I have the FijiFilm x100vi and love it because of the film recipes (and 40M pixel sensor). I'm finally getting around to learning more about making your own custom recipes (see here for a whole bunch of information).

Examples

If you are unfamiliar with this camera, you can set up "recipes" that mimic historical film stocks. The cool thing is that you can put the parameters in the camera and then just switch between them for each image, and it saves the output as JPG (and RAW) with that recipe (no need for a bunch of editing).

Here are some examples.

Kodachrome

Just like the song says,

Kodachrome
They give us those nice bright colors
They give us the greens of summers
Makes you think all the world’s
A sunny day, oh yeah

This is a recipe that emphasizes that look.

Classic Cuban

For example, this photo was taken with a custom recipe I modified called Classic Cuban, and it is one that I've put in for my upcoming trip to Islamorada to go fishing with my father.

Or a B/W Filmstock

Here is a simulation of Ilford Delta 400.

Problem

One thing that bothered me was that both Apple Photos and Adobe Lightroom failed to read the EXIF data containing the recipe specifics. It would be great to have them as part of the file information, saved in a location accessible to these two programs (for searching, etc.).

So let's do this.

Extracting Information

To do this, I'll first start with a small shell script to go harvest this information for me. If you do not have exiftool installed on your computer, you can grab it from brew as:

brew install exiftool

Then we want to grab the specific information from the JPG metadata and

exiftool -FilmMode -WhiteBalance -WhiteBalanceFineTune \ 
    -HighlightTone -ShadowTone -ClaritySetting -ColorChromeEffect \
    -ColorChromeFXBlue file.JPG
#!/bin/bash

# Input check
if [ -z "$1" ]; then
  echo "Usage: $0 <image.jpg|image.raf>"
  exit 1
fi

FILE="$1"

# Ensure file exists
if [ ! -f "$FILE" ]; then
  echo "File not found: $FILE"
  exit 1
fi

# Extract recipe-related fields
FILM_SIM=$(exiftool -s3 -FilmMode "$FILE")
WB_MODE=$(exiftool -s3 -WhiteBalance "$FILE")
WB_SHIFT=$(exiftool -s3 -WhiteBalanceFineTune "$FILE")
HIGHLIGHT=$(exiftool -s3 -HighlightTone "$FILE")
SHADOW=$(exiftool -s3 -ShadowTone "$FILE")
COLOR=$(exiftool -s3 -ColorTone "$FILE")
SHARPNESS=$(exiftool -s3 -Sharpness "$FILE")
CLARITY=$(exiftool -s3 -ClaritySetting "$FILE")
GRAIN=$(exiftool -s3 -GrainEffect "$FILE")
CHROME=$(exiftool -s3 -ColorChromeEffect "$FILE")
CHROME_BLUE=$(exiftool -s3 -ColorChromeFXBlue "$FILE")
DRANGE=$(exiftool -s3 -DynamicRange "$FILE")

# Format the description text
DESC="Film Simulation: ${FILM_SIM:-N/A}, WB: ${WB_MODE:-N/A} (${WB_SHIFT:-N/A}), Highlight: ${HIGHLIGHT:-N/A}, Shadow: ${SHADOW:-N/A}, Clarity: ${CLARITY:-N/A}, Color: ${COLOR:-N/A}, Sharpness: ${SHARPNESS:-N/A}, Grain: ${GRAIN:-N/A}, DR: ${DRANGE:-N/A}, CC FX: ${CHROME:-N/A}, CC FX Blue: ${CHROME_BLUE:-N/A}"

# Create keyword list (remove special chars and normalize spacing)
KEYWORDS="FujiFilm, $FILM_SIM, WB:$WB_MODE, DR:$DRANGE, CC:$CHROME, CCB:$CHROME_BLUE"
KEYWORDS_CLEAN=$(echo "$KEYWORDS" | sed 's/[^a-zA-Z0-9,: -]//g' | tr -s ' ')

# Apply the metadata
echo "Writing metadata to $FILE..."
exiftool \
  -overwrite_original \
  -XMP:Description="$DESC" \
  -XMP:Subject="$KEYWORDS_CLEAN" \
  "$FILE"

echo "Done."

That is a good first start. However, in this file, there was nothing set for a few of these parameters, and as such, we are now adding Parameter: NA to the Descriptions, and I'd like to minimize the extra information I'm shoving into these fields. So here is an addition that only adds the value for a parameter if it is present.

#!/bin/bash

# Usage: script.sh file.JPG/file.RAW

# Input check
if [ -z "$1" ]; then
  echo "Usage: $0 <image.jpg|image.raf>"
  exit 1
fi

FILE="$1"

if [ ! -f "$FILE" ]; then
  echo "File not found: $FILE"
  exit 1
fi

# Function to safely extract and conditionally format metadata
add_if_present() {
  local label="$1"
  local value="$2"
  if [[ -n "$value" && "$value" != "N/A" ]]; then
    echo "$label: $value"
  fi
}

# Extract fields
FILM_SIM=$(exiftool -s3 -FilmMode "$FILE")
WB_MODE=$(exiftool -s3 -WhiteBalance "$FILE")
WB_SHIFT=$(exiftool -s3 -WhiteBalanceFineTune "$FILE")
HIGHLIGHT=$(exiftool -s3 -HighlightTone "$FILE")
SHADOW=$(exiftool -s3 -ShadowTone "$FILE")
COLOR=$(exiftool -s3 -ColorTone "$FILE")
SHARPNESS=$(exiftool -s3 -Sharpness "$FILE")
CLARITY=$(exiftool -s3 -ClaritySetting "$FILE")
GRAIN=$(exiftool -s3 -GrainEffect "$FILE")
CHROME=$(exiftool -s3 -ColorChromeEffect "$FILE")
CHROME_BLUE=$(exiftool -s3 -ColorChromeFXBlue "$FILE")
DRANGE=$(exiftool -s3 -DynamicRange "$FILE")

# Build description string line-by-line
DESC=""
DESC+=$(add_if_present "Film Simulation" "$FILM_SIM")$'\n'
DESC+=$(add_if_present "WB" "$WB_MODE")$'\n'
DESC+=$(add_if_present "WB Shift" "$WB_SHIFT")$'\n'
DESC+=$(add_if_present "Highlight" "$HIGHLIGHT")$'\n'
DESC+=$(add_if_present "Shadow" "$SHADOW")$'\n'
DESC+=$(add_if_present "Clarity" "$CLARITY")$'\n'
DESC+=$(add_if_present "Color" "$COLOR")$'\n'
DESC+=$(add_if_present "Sharpness" "$SHARPNESS")$'\n'
DESC+=$(add_if_present "Grain" "$GRAIN")$'\n'
DESC+=$(add_if_present "Dynamic Range" "$DRANGE")$'\n'
DESC+=$(add_if_present "CC FX" "$CHROME")$'\n'
DESC+=$(add_if_present "CC FX Blue" "$CHROME_BLUE")$'\n'

# Remove empty lines
DESC_CLEAN=$(echo "$DESC" | sed '/^$/d' | tr '\n' ', ' | sed 's/, $//')

# Keywords (limited to high-level searchable terms)
KEYWORDS="FujiFilm"
[[ -n "$FILM_SIM" && "$FILM_SIM" != "N/A" ]] && KEYWORDS+=", $FILM_SIM"
[[ -n "$WB_MODE" && "$WB_MODE" != "N/A" ]] && KEYWORDS+=", WB:$WB_MODE"
[[ -n "$DRANGE" && "$DRANGE" != "N/A" ]] && KEYWORDS+=", DR:$DRANGE"
[[ -n "$CHROME" && "$CHROME" != "N/A" ]] && KEYWORDS+=", CC:$CHROME"
[[ -n "$CHROME_BLUE" && "$CHROME_BLUE" != "N/A" ]] && KEYWORDS+=", CCB:$CHROME_BLUE"

# Clean extra whitespace
KEYWORDS_CLEAN=$(echo "$KEYWORDS" | sed 's/[^a-zA-Z0-9,: +-]//g' | tr -s ' ')

# Write back metadata
echo "Writing metadata to $FILE"
exiftool \
  -overwrite_original \
  -XMP:Description="$DESC_CLEAN" \
  -XMP:Subject="$KEYWORDS_CLEAN" \
  "$FILE"

echo "Metadata updated."

Nice. I put it in ~/Applications/bin and made sure this script is both executable and its path is in my $PATH variable.

Making it Work from Within Apple Photos

The challenge is that the image needs to be accessible to the script (e.g., in a file of some sort), and it would be really nice if we could get it to work by adding an Apple shortcut to do the work on the image while it is still within Apple Photos...