Skip to the content.

read_image_patch_colors

Reference and Usage Guide

Version: 1.5
Author: Knut Larsson
Purpose: Generate ArgyllCMS compatible .ti1 and .ti2 files from a grid of color patches in an image.

Table of Contents

Overview

This program extracts color values from a rectangular grid of color patches in an image, computes colorimetric values (RGB percentages, XYZ, Lab and approximate white point), applies row/column labeling rules, and writes three output files:

It supports sampling in mean or median mode, configurable patch geometry, numeric or alphabetic labeling patterns, and RGB/XYZ/Lab output combinations.

The script is designed for use in color-management workflows where printed color targets are scanned or photographed and converted into Argyll measurement files. Alternatively, convert original calibration target images to attain .ti1, .ti2 and .csv files with their patch color data.

Use Cases

Example use cases for reading the image of a reference target:

  1. Create a .ti1 file so that one may use ArgyllCMS printtarg command and generate a target using the colors of the image, which then can be used with ArgyllCMS chartread and colprof to create a printer profile.
    • Workflow:
      Read image with script → input .ti1 to printtarg → Print generated Target image without color management → input .ti2 to chartread then read target patches with colorimeter → input .ti3 to colprof → output profile.
  2. Create a .ti2 file so that one may use ArgyllCMS chartread, and use a colorimeter to read the color values using the original SpyderPrint Targets, which then can be used with ArgyllCMS colprof to create a printer profile.
    • Workflow:
      Read image with script → Print original SpyderPrint Target image without color management → input .ti2 to chartread then read target patches with colorimeter → input .ti3 to colprof → output profile.
  3. Create a .ti1 file so that one may use ArgyllCMS fakeread command using colors of the image, which then can be used with ArgyllCMS colprof to create a simulated profile.
    • Workflow:
      Read image with script → input .ti1 to fakeread → input .ti3 to colprof → output simulated profile.

Examples Included

See chapter Examples for detailed examples on how to read patches from different size patch grid images. All examples are based on Datacolor SpyderPrint Targets, which are:

Target images used for creating .ti1, .ti2 and .csv files are under folder Example Targets Read.

Several examples of generating an ArgyllCMS printtarg targets for SpyderPrint Targets are also included.

Command-line Arguments

Argument Description
--image / -i (Required) Path to the input image containing the color-patch grid. Image must be a RGB image used as target for calibration. See more detalis in section Image Input.
--pre_cond_profile ICC/ICM profile file path, used as device pre-conditioning profile.
Default: sRGB.icm.
This adapts/shapes the XYZ and LAB values to a known device/printer profile or color space profile. This also affects the approx. white point value stored in .ti1 and .ti2 files. RGB values are unaffected. For simplicity, place icc file in same folder as script.

Supported path formats:
→ MyProfile.icm (current folder)
→ /System/Library/ColorSync/ Profiles/MyProfile.icc
→ ./profiles/AdobeRGB1998.icc (Current folder is ref.)
→ ../MyProfile.icc (one folder up)
→ C:\Windows\MyProfile.icm
→ C:/Color/MyProfile.icm
→ /usr/share/color/icc/MyProfile.icc
--patch_first_xy (Required) "X,Y" coordinates of the center of the first patch (top-left). Accepts integers or floats.
--patch_last_xy (Required) "X,Y" coordinates of the center of the last patch (bottom-right). Accepts integers or floats.
Note: In some cases some patches on last row of image may not exist. In these cases, coordinates must be to the last row and column, as if it would exist. After generating of .ti1, .ti2 and .csv files the last patches must be manually removed, if output must be exact like original image. If patch_label_order is col_then_row then the patces to remove are in sequence at the end of the list, but if row_then_col is used, then you would need to search for the patch lables (SAMPLE_LOC) to locate them and remove them.
--patch_width_height_ratio (Required) Width ÷ Height ratio (W/H) of a single patch.
Examples: 1.0 (square), 1.378, etc.
--num_cols (Required) Number of columns in the grid.
--num_rows (Required) Number of rows in the grid.
--sample_fraction Fraction of patch area to sample (float). Default: 0.50 (50%).
Constraints: 0 < f ≤ 0.6.
The sampling area is a centered square clamped to at least 3×3 px and at most 60% of patch size.
--row_labels (Required) Label sequence for rows. Must match num_rows exactly.
Allowed patterns:
1-15 Numeric range
03-12 Numeric range (zero-padding preserved)
0001-0120 Numeric range (zero-padding preserved)
A-Z Alphabetic range
A-AC Alphabetic range
BQ-CF Alphabetic range
--col_labels (Required) Label sequence for columns (same rules as row_labels).
--patch_label_order (Required) Determines patch label composition. col refer here to the defined col_labels, and row to the defined row_labels. The patch label order is independent of the specified output_order.

Examples, assuming row_labels are numeric and col_labels are alphabetic:
col_then_row → e.g. A12
row_then_col → e.g. 12A
--output_order Determines initial traversal order, i.e. how the grid shall be read when converting to the output-list of color data. Traversal always starts at top-left corner patch in the grid. Serves as basis before rotation/mirroring:
row_major(default) → iterate row by row (top→bottom, then left→right)
column_major → iterate column by column (left→right, then top→bottom)
--mirror_output Reverse traversal after output_order and rotate_grid. Only applied if specified:
output_order setting:
If row_major → reverse column values for each row (vertical flip)
If column_major → reverse row values for each column (horizontal flip)
--rotate_grid Rotate the entire patch grid clockwise before mirroring (if mirror_output applied). Allowed values:
0 (default)
90 → rotate 90° CW
180 → rotate 180° CW
270 → rotate 270° CW
Applied after output_order, before mirror_output.
--output_color_space (Required) Comma-separated list specifying which color spaces to output (in order). Allowed tokens:
RGB
XYZ
LAB
Rules:
• TI1 can include only RGB and/or XYZ
• TI2/CSV include all listed tokens
Examples:
RGB,XYZ → TI1: RGB,XYZ; TI2: RGB,XYZ.
XYZ,LAB,RGB → TI1: XYZ,RGB; TI2: XYZ,LAB,RGB.
--sample_mode Patch sampling aggregation method:
mad(default) → robust mean via MAD-based sigma clipping
mean → arithmetic mean (not robust)
median → robust, but biases asymmetric patches
--output Base filename and/or path for generated .ti1, .ti2, and .csv output files.
If omitted, filenames are based on the input image name and placed in the same folder as the script. If path is specified, the script will place all output files in path. If the specified path also includes a filename, the script will use that filename for all output files.
--output "./Outputfolder/"Only directory, current folder is reference (keep input image name)
--output "./Outputfolder/ Outputfilename"Directory + filename, current folder is reference (override input image name)
--output "User/Username/ Outputfolder/Outputfilename"Directory + filename (override input image name)
--output "../Outputfolder/"Directory in parallel folder, one up in structure (keep input image name)
--debug Enable diagnostic printing for the first few patches. Shows sampling geometry, pixel statistics, and intermediate RGB calculations.

Image Input

It is expected that the image:

The argument --pre_cond_profile is set to sRGB color space profile as default, which generates XYZ and LAB colors adapted to this profile with D50 reference white. RGB values are unaffected.

Currently, the output .ti1 file from this script is hard coded with COLOR_REP=iRGB, assuming that the intended printer used prints RGB (additive), but behind the driver is a subtractive CMYK printer (corresponding to ArgyllCMS targen argument -d2).

It is possible to read images with other white points and apply any other color space encoding, but the output .ti1 and .ti2 files will reflect this. If the color values are acceptable, the .ti1 may still be used with ArgyllCMS printtarg to generate targets, but the .ti2 will probably not be usable in calibration workflows together with the read image.

Accepted image types: any PIL-compatible raster file Color depth: handled as 16-bit RGB internally

Parameter: --image / -i Provides the path to the patch-grid image.

Grid Geometry

Geometry is computed from:

The script calculates patch center coordinates and boundaries.

Sampling

Parameter: --sample_fraction Range: 0 < f ≤ 0.6 Defines the fraction of the smaller patch dimension used for sampling.

Sampling modes (parameter --sample_mode):

Sampling region is square, centered in each patch.

Labeling Rules

Row and column labels come from:

Accepted patterns:

Label order:

Label counts must match grid dimensions exactly.

Color Processing

Sampled RGB → RGB percentage (0–100): RGB_percent = (R_16bit / 65535) * 100

Color conversions (XYZ, Lab) are computed through ArgyllCMS xicclu command with selected color icc profile (default sRGB.icm). Absolute colorimetric intent with D50 illuminant is used, same as how ArgyllCMS targen creates patch colors, and is expected by printtarg and chartread. Targen uses a sRGB-like model for generating XYZ and LAB D50, unless another profile i provided. This is why sRGB.icm color space profile is used by default, but may be overridden by choosing a different profile with --pre_cond_profile.

XYZ output:

Lab output:

The patch with the highest Y (XYZ_Y) is stored as APPROX_WHITE_POINT. Note that XYZ Absolute colorimetric D50 is almost identical to XYZ Relative colorimetric D65, which can confuse some users to think that the APPROX_WHITE_POINT is computed using D65. This is not the case.

Output Files

Three files:

1. TI1 (.ti1)

Contains:

• Header (descriptor, originator, timestamp)
• COLOR_REP (always "iRGB")
• ACCURATE_EXPECTED_VALUES flag
• Data fields (SAMPLE_ID, RGB, optionally XYZ)
• Patch data in Argyll .ti1 format (SAMPLE_ID, RGB and optionally XYZ)
• Dynamically generated:
    • APPROX_WHITE_POINT
    • WHITE_COLOR_PATCHES
    • BLACK_COLOR_PATCHES
    • COMP_GREY_STEPS
    • SINGLE_DIM_STEPS
    • NUMBER_OF_FIELDS
    • NUMBER_OF_SETS
    • DENSITY_EXTREME_VALUES table
    • DEVICE_COMBINATION_VALUES table

DENSITY_EXTREME_VALUES and DEVICE_COMBINATION_VALUES are based on static RGB values commonly used by ArgyllCMS targen, but XYZ values are made using supplied ICC profile. Also, no ink limiting is applied.

Tags containing a dynamically calculated number are omitted if 0 (zero).

COMP_GREY_STEPS

Number represents count of patches with equal RGB channel values, basically neutral colors. Note: This is not the NEUTRAL_STEPS tag, as they would be generated by ArgyllCMS targen taking into acount the neutral axis for a given icc profile. This script does not support creating NEUTRAL_STEPS count.

RGB Balance Check:

SINGLE_DIM_STEPS

This number is estimated by reading single-dimension color ramps where two RGB channels remain static while the third creates a ramp pattern. The number represents the most commonly detected number of steps for single-channel ramps. A single-channel ramp is defined as a sequence where:

Note: if merging two or more .ti1 files the value of this field is not added like other tags. Instead, use the largest number in the merged file.

2. TI2 (.ti2)

Contains:

• Header (descriptor, originator, timestamp)
• STRIP and PATCH index patterns inferred from row/col labels
• STEPS_IN_PASS     = num_rows
• PASSES_IN_STRIPS2 = num_cols
• Index order       = STRIP_THEN_PATCH
• Dynamically generated:
    • APPROX_WHITE_POINT
    • NUMBER_OF_FIELDS
    • NUMBER_OF_SETS
• Complete patch data (SAMPLE_ID, SAMPLE_LOC, RGB, XYZ, Lab)

SAMPLE_LOC corresponds to labels such as “A1”, “D14”, “C07”, etc.

3. CSV (.csv)

Space-separated table with SAMPLE_ID, SAMPLE_LOC, and selected color values.

Patch Value Ranges

RGB: 0–100
XYZ: typically 0–100
Lab_L: 0–100
Lab_a/b: approx. –128 to +128

All values have six decimal places.

Valid Color Blocks

--output_color_space must include at least one of: RGB, XYZ, LAB

Example: --output_color_space RGB,XYZ

Order controls column order in TI1/TI2/CSV.

Dependencies

Notes on Accuracy

Patch Sampling Method

For each patch:

  1. Patch center (Cx, Cy) is computed by linear interpolation between patch_first_xy → patch_last_xy across the grid.
  2. A square sampling region of size sample_size × sample_size (odd number, min 3, max 60% of patch) is centered at (Cx, Cy).
  3. The script extracts all pixels inside this region.
  4. The sampled RGB values are averaged (MAD-based sigma) in 16-bit integer space (8-bit inputs are scaled to 16-bit via *257).
  5. Color conversions are applied using xicclu:
    • RGB -> CIEXYZ XYZ (D50)
    • RGB -> Lab (D50)

Output File Format (TI2)

CTI2

DESCRIPTOR "Argyll Calibration Target chart information 2"
ORIGINATOR "read_image_patch_colors.py"
CREATED "<timestamp>"
ACCURATE_EXPECTED_VALUES "true"
APPROX_WHITE_POINT "<Xw Yw Zw>"
COLOR_REP "iRGB"
STEPS_IN_PASS "<ROWS>"
PASSES_IN_STRIPS2 "<COLS>"
STRIP_INDEX_PATTERN "<row_pattern>"
PATCH_INDEX_PATTERN "<column_pattern>"
INDEX_ORDER "STRIP_THEN_PATCH"

NUMBER_OF_FIELDS <AAA>
BEGIN_DATA_FORMAT
SAMPLE_ID SAMPLE_LOC [color fields...]
END_DATA_FORMAT

NUMBER_OF_SETS <BBB>
BEGIN_DATA
  <patch_index> "<label>" <RGB/XYZ/Lab values...>
  ...
END_DATA

Where:

Fields appear in the order defined by output_color_space.

Examples:

Error Conditions

Use --debug flag to investigate issues relating to color on patches.

Script stops if:

Workflow Summary

  1. Validate dependencies
  2. Parse CLI args
  3. Load image
  4. Compute grid geometry
  5. Generate labels
  6. Sample patches
  7. Convert RGB→XYZ→Lab
  8. Compute white/black points
  9. Generate metadata
  10. Write TI1/TI2/CSV

Examples

9×12 SpyderPrint target with numeric rows, alphabetic columns

python3 read_image_patch_colors.py \
  --image "EZ 729 Colors Plus Grays 1 of 9 (108-patches).tif" \
  --patch_first_xy 111,64 \
  --patch_last_xy 1039,746 \
  --patch_width_height_ratio 1.89286 \
  --num_cols 9 \
  --num_rows 12 \
  --row_labels 1-12 \
  --col_labels A-I \
  --patch_label_order col_then_row \
  --output_color_space RGB,XYZ

10×12 SpyderPrint target with numeric rows, alphabetic columns

python3 read_image_patch_colors.py \
  --image "EZ 729 Colors Plus Grays 8 of 9 (120-patches).tif" \
  --patch_first_xy 87,67 \
  --patch_last_xy 1023,749 \
  --patch_width_height_ratio 1.7142857 \
  --num_cols 10 \
  --num_rows 12 \
  --row_labels 1-12 \
  --col_labels A-J \
  --patch_label_order col_then_row \
  --output_color_space RGB,XYZ

15×15 SpyderPrint target with numeric rows, alphabetic columns

python3 read_image_patch_colors.py \
  --image "High Quality Target (225-patches).tif" \
  --patch_first_xy 61,50 \
  --patch_last_xy 848,612 \
  --patch_width_height_ratio 1.37837838 \
  --num_cols 15 \
  --num_rows 15 \
  --row_labels 1-15 \
  --col_labels A-O \
  --patch_label_order col_then_row \
  --output_color_space RGB,XYZ

17×14 SpyderPrint target with numeric rows, alphabetic columns

python3 read_image_patch_colors.py \
  --image "Expert Target Plus Grays 4 of 4 (238-patches).tif" \
  --patch_first_xy 56,51 \
  --patch_last_xy 847,592 \
  --patch_width_height_ratio 1.09803921568 \
  --num_cols 17 \
  --num_rows 14 \
  --row_labels 1-14 \
  --col_labels A-Q \
  --patch_label_order col_then_row \
  --output_color_space RGB,XYZ

18×14 SpyderPrint target with numeric rows, alphabetic columns

Note!
This script requires specification of first and last patch of a full grid. Thus, last 9 patches of last row must be manually removed from files as they do not exist in image.

python3 read_image_patch_colors.py \
  --image "Expert Target Page 1 of 3 (243-patches).tif" \
  --patch_first_xy 48,45 \
  --patch_last_xy 872,604 \
  --patch_width_height_ratio 1.075 \
  --num_cols 18 \
  --num_rows 14 \
  --row_labels 1-14 \
  --col_labels A-R \
  --patch_label_order col_then_row \
  --output_color_space RGB,XYZ

27×27 SpyderPrint target with numeric rows, alphabetic columns

Note!
This image uses a special character on last column label, which must be manually changed in the ti2 file.

python3 read_image_patch_colors.py \
  --image "Expert Target (large)(729-patches).tif" \
  --patch_first_xy 72,51 \
  --patch_last_xy 1250,934 \
  --patch_width_height_ratio 1.4 \
  --num_cols 27 \
  --num_rows 27 \
  --row_labels 1-27 \
  --col_labels A-AA \
  --patch_label_order col_then_row \
  --output_color_space RGB,XYZ

36×26 LaserSoft target with numeric rows, alphabetic columns

python3 read_image_patch_colors.py \
  --image "LaserSoft Advanced ISO12641-2 Target (864-patches).tif" \
  --patch_first_xy 168,168 \
  --patch_last_xy 1335,935 \
  --patch_width_height_ratio 1.0 \
  --num_cols 36 \
  --num_rows 24 \
  --row_labels A-X \
  --col_labels 1-36 \
  --patch_label_order row_then_col \
  --output_color_space RGB,XYZ