The Ultimate Guide to RAW Development: Data-Driven Insights (SILKYPIX, Pixelmator Pro, and GR IIIx JPEG Compared)
Introduction
I’ve been using SILKYPIX to develop RAW images captured with my RICOH GR IIIx. However, I’ve recently started wondering: Is it really worth continuing to use this software?
There are two main reasons for my hesitation:
- The processing takes a significant amount of time.
- The preview feature is sluggish, and the software frequently freezes after extended use.
With macOS now supporting RAW files natively, a variety of apps are available for editing these files. To explore my options, I wrote a Python-based image comparison tool and analyzed three images to see how they stack up:
- Image 1: A JPEG file developed using the macOS version of SILKYPIX, without any adjustments.
- Image 2: A JPEG file developed using Pixelmator Pro, also without adjustments. Pixelmator Pro takes advantage of macOS’s native RAW support, ensuring high compatibility and efficient processing of RAW files.
- Image 3: A JPEG image saved directly from the GR IIIx at the time of capture, with the “Negative Film” preset applied via Image Control.
1. Analyzing the JPEG Files Using Data
The image I used for comparison was a park scene with autumn leaves illuminated by the setting sun.
Here are the results from my Python program:
❯ python three-image-comparison.py
Image 1 Analysis:
Size: (4000, 6000, 3)
Mean Brightness: 85.267
Standard Deviation: 72.58
Mean Saturation: 113.675
Contrast: 255
File Size(KB): 19070.3955078125
Image 2 Analysis:
Size: (4000, 6000, 3)
Mean Brightness: 66.147
Standard Deviation: 73.611
Mean Saturation: 135.691
Contrast: 255
File Size(KB): 29992.3681640625
Image 3 Analysis:
Size: (4000, 6000, 3)
Mean Brightness: 93.122
Standard Deviation: 74.255
Mean Saturation: 85.623
Contrast: 255
File Size(KB): 13395.0634765625
Comparison Results:
SSIM 1-2: 0.68
SSIM 1-3: 0.847
SSIM 2-3: 0.737
Histogram Correlation 1-2: 0.3822719180861183
Histogram Correlation 1-3: 0.7985840630919663
Histogram Correlation 2-3: 0.13442670111231494
2. Insights from the Image Comparison
2.1. Image Comparison Results
The characteristics of each image based on the data are as follows:
- SILKYPIX Image
- Moderate brightness with a calm tone.
- Adequate saturation, achieving a balance of vibrancy and naturalness.
- Stable brightness, resulting in a uniform finish.
- Pixelmator Pro Image
- The darkest output, emphasizing shadows.
- The highest saturation, focusing on vibrancy.
- Dynamic tonal changes, creating a dramatic impression.
- GR IIIx Jpeg Image (Negative Film Style)
- The brightest output, with a light and soft tone.
- The lowest saturation, offering a calm and natural color palette.
- Moderate brightness variation, providing smooth and natural gradation.
From the comparison results, the following observations can be made:
- Based on SSIM (Structural Similarity Index), SILKYPIX and Pixelmator Pro exhibit significantly different structures, with notable differences in brightness and saturation. The GR IIIx Jpeg is relatively similar to SILKYPIX but with more subdued saturation.
- According to the Histogram Correlation, SILKYPIX and Pixelmator Pro show distinctly different color distributions. When compared to GR IIIx Jpeg, SILKYPIX has some similarity, while Pixelmator Pro displays significant differences.
2.2. Evaluating RAW Development Apps
Based on the analysis, here’s my evaluation of each tool:
-
SILKYPIX
SILKYPIX delivers vivid colors and a balanced brightness level, making it ideal for landscapes or scenes where vibrant colors are essential. Its versatility makes it suitable for various scenarios. -
Pixelmator Pro
With its darker tones and shadow emphasis, Pixelmator Pro works well for dramatic compositions or print projects. However, additional adjustments might be required for darker areas. -
GR IIIx JPEG (Negative Film Preset)
The GR IIIx JPEG provides a bright, soft tone with natural colors, making it great for everyday photography and relaxed scenes.
3. Final Thoughts
From this comparison, SILKYPIX proves to be a reliable tool, and the numerical data supports its strengths. Surprisingly, I found myself drawn to the GR IIIx JPEG with the Negative Film preset.
A practical workflow could be to shoot in RAW + JPEG mode, using the JPEGs directly for most situations and turning to SILKYPIX for advanced adjustments when needed.
Appendix. Python Program for Image Analysis
Here is the Python program I used for this analysis. It’s a quick and simple script, so it includes hard-coded file names for convenience. Feel free to adapt it to your needs.
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from skimage.metrics import structural_similarity as ssim
import cv2
import os
def compare_three_images(image1_path, image2_path, image3_path, output_path=None):
"""
Compare three images and analyze them using various metrics
Parameters:
image1_path (str): Path to first image
image2_path (str): Path to second image
image3_path (str): Path to third image
output_path (str): Path to save comparison results (optional)
Returns:
dict: Comparison metrics
"""
# Load images
img1 = cv2.imread(image1_path, cv2.IMREAD_UNCHANGED)
img2 = cv2.imread(image2_path, cv2.IMREAD_UNCHANGED)
img3 = cv2.imread(image3_path, cv2.IMREAD_UNCHANGED)
if img1 is None or img2 is None or img3 is None:
raise ValueError("Failed to load one or more images")
# Normalize to 8-bit if necessary for visualization
def normalize_to_8bit(img):
if img.dtype != np.uint8:
return cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
return img
img1_8bit = normalize_to_8bit(img1)
img2_8bit = normalize_to_8bit(img2)
img3_8bit = normalize_to_8bit(img3)
# Convert to grayscale for SSIM
gray1 = cv2.cvtColor(img1_8bit, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2_8bit, cv2.COLOR_BGR2GRAY)
gray3 = cv2.cvtColor(img3_8bit, cv2.COLOR_BGR2GRAY)
# Calculate SSIM between each pair
ssim_1_2 = ssim(gray1, gray2)
ssim_1_3 = ssim(gray1, gray3)
ssim_2_3 = ssim(gray2, gray3)
# Compare histograms between each pair
def calculate_hist_correlation(img1, img2):
hist1 = cv2.calcHist([img1], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])
return cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
hist_corr_1_2 = calculate_hist_correlation(img1_8bit, img2_8bit)
hist_corr_1_3 = calculate_hist_correlation(img1_8bit, img3_8bit)
hist_corr_2_3 = calculate_hist_correlation(img2_8bit, img3_8bit)
# Visualization
plt.figure(figsize=(15, 10))
# Images
plt.subplot(231)
plt.imshow(cv2.cvtColor(img1_8bit, cv2.COLOR_BGR2RGB))
plt.title("Image 1")
plt.axis("off")
plt.subplot(232)
plt.imshow(cv2.cvtColor(img2_8bit, cv2.COLOR_BGR2RGB))
plt.title("Image 2")
plt.axis("off")
plt.subplot(233)
plt.imshow(cv2.cvtColor(img3_8bit, cv2.COLOR_BGR2RGB))
plt.title("Image 3")
plt.axis("off")
# Histograms
plt.subplot(234)
plt.hist(img1_8bit.ravel(), 256, [0, 256])
plt.title("Histogram - Image 1")
plt.subplot(235)
plt.hist(img2_8bit.ravel(), 256, [0, 256])
plt.title("Histogram - Image 2")
plt.subplot(236)
plt.hist(img3_8bit.ravel(), 256, [0, 256])
plt.title("Histogram - Image 3")
if output_path:
plt.savefig(output_path)
plt.close()
return {
"SSIM 1-2": ssim_1_2,
"SSIM 1-3": ssim_1_3,
"SSIM 2-3": ssim_2_3,
"Histogram Correlation 1-2": hist_corr_1_2,
"Histogram Correlation 1-3": hist_corr_1_3,
"Histogram Correlation 2-3": hist_corr_2_3,
"Size": img1.shape,
"Bit Depth 1": str(img1.dtype),
"Bit Depth 2": str(img2.dtype),
"Bit Depth 3": str(img3.dtype),
}
def analyze_image(image_path):
"""
Analyze one image
Parameters:
image_path (str): path to the image
Returns:
dict: results of analysis
"""
img = cv2.imread(image_path)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
return {
"Size": img.shape,
"Mean Brightness": np.mean(img),
"Standard Deviation": np.std(img),
"Mean Saturation": np.mean(hsv[:, :, 1]),
"Contrast": np.max(img) - np.min(img),
"File Size(KB)": os.path.getsize(image_path) / 1024, # KB単位
}
def format_results(results_dict):
"""
Format the results dictionary for better readability
"""
formatted = {}
for key, value in results_dict.items():
if isinstance(value, (np.float64, np.float32)):
formatted[key] = float(round(value, 3))
elif isinstance(value, np.uint8):
formatted[key] = int(value)
else:
formatted[key] = value
return formatted
if __name__ == "__main__":
# Compare three images
results = compare_three_images(
"image1.jpg", "image2.jpg", "image3.jpg", "comparison_result.png"
)
# Analyze each image
images = ["image1.jpg", "image2.jpg", "image3.jpg"]
for i, image_path in enumerate(images, 1):
analysis = analyze_image(image_path)
print(f"\nImage {i} Analysis:")
for key, value in format_results(analysis).items():
print(f"{key}: {value}")
print("\nComparison Results:")
for key, value in format_results(results).items():
print(f"{key}: {value}")