This is a reference for the DX latent edge barcode printed along the edge of 35mm film. It is the optical edge code standardized under PIMA / ANSI IT3.42 / ISO 1007, not the DX cartridge code (the 12 metal contacts on the cartridge body, Kodak's "DX-CAS", or DX Camera Auto-Sensing).
This page is about the cell structure: physical layout, sentinels, field positions, and how the cells are read. Mapping a film stock to ISO is a separate lookup-table problem using Part 1 / Part 2; see Lookup tables.
Most online references to 35mm "DX codes" are about the cartridge code: the 12 metal contacts on the cartridge body. They usually cover DIY changes to refillable canisters, or intentional mis-coding to make an automatic camera meter at a different ISO. That is a separate electrical encoding read by cameras.
The latent edge code is the optical barcode on the film itself, read by photofinishers and scanners. I found public documentation for its bit-level format much harder to come by. The formal standards (ISO 1007 and the US lineage ANSI PH1.14 → NAPM IT1.14 → PIMA IT3.42) are sold by ISO / ANSI and do not appear to provide a free technical preview. The original 1990 Kodak patent (US 4,965,628) is available on Google Patents, and describes the structural format, but it does not fully specify details such as parity polarity, half-frame polarity, or MSB-vs-LSB ordering.
This page collects the format details I could tie back to the patent and public cross-checks: a working open-source decoder and empirical decodes from known film strips.
Sources used for the details below:
- Structural source — Kodak patent US 4,965,628 (Oliver, Johnson, Patton, 1990) — defines the per-half-frame barcode structure, field positions, and reference numbers (510 = clock entry, 515 = film product class, 520 = film specifier, 525 = frame no., 1/2 frame flag, 530 = parity, 535 = clock exit). FIG. 5 shows the clock track pattern and field layout. Available on Google Patents.
- Polarity / bit-ordering source — ISO 1007 / ANSI IT3.42. Details the patent does not pin down: even vs odd parity, which half-frame value means "A", MSB-first vs LSB-first bit ordering within multi-bit fields — are defined in these standards. I have not quoted the standards directly here; the values below come from the cross-checks in the next bullet.
- Cross-checks for polarity / bit-ordering — two public sources agree:
- zxing-cpp's
ODDXFilmEdgeReader.cpp, a working open-source decoder - Empirical decodes of known-content strips (e.g., thebigfilmdatabase.merinorus.com)
- zxing-cpp's
The code is printed in the narrow strip between the lower perforation row and the bottom edge of the film. It is not in the central image area between the two perforation rows. It is exposed as a latent image during manufacture and becomes visible after development.
┌────────────────────────────────────────────┐ ← top edge of film
│ eye-readable text strip │ ← film type, frame numbers
│ □ □ □ □ □ □ □ □ □ □ □ │ ← top perforation row
│ │
│ │
│ │
│ IMAGE AREA │ ← image frame area
│ │
│ │
│ │
│ □ □ □ □ □ □ □ □ □ □ □ │ ← bottom perforation row
│ DX edge code strip │ ← barcode lives here
└────────────────────────────────────────────┘ ← bottom edge of film
Frame numbers and other human-readable labels are usually in the corresponding strip on the opposite edge, between the top perforation row and the top edge. In other words, the barcodes and eye-readable digits are on opposite long edges.
Two parallel tracks run along the film's long axis:
- Clock track (top, data-independent reference)
- Data track (bottom, per-frame variable data)
Each cell in a track is binary: either an exposed mark (dark/dense on a developed negative) or an unexposed gap (clear film base on the negative).
In the diagrams below:
█ = mark (exposed, dark on negative)
░ = gap (unexposed film base, clear on negative)
Each code repetition is 31 cells wide. The code repeats approximately every 19mm along the film. The camera's frame-advance pitch is approximately 38mm, so each image frame roughly contains two code repetitions: usually one whole-frame label and one half-frame label (the "A" suffix on Kodak rolls). The two pitches share the same 2:1 ratio, which is why this lines up.
The edge code is imprinted at the factory at a fixed pitch. Image frames
are exposed later by the camera, at whatever phase results from loading
the roll. The two are not phase-locked. A given image frame might line
up with an N/NA pair, straddle the boundary between 5A and 6, or
fall somewhere else entirely. When matching DX codes to scanned images,
do not assume "this image frame = this DX frame label"; the phase comes
from how the roll was loaded.
A reader observes both tracks simultaneously and decodes them as a 31-cell × 2-track binary matrix.
cell index: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
↑─── entry sentinel ───↑ ↑──────────────── data area (21 cells) ─────────────────↑ ↑─ exit sentinel ──↑
The clock track has a fixed 31-cell pattern, identical in every code on every film stock. Shown as it appears on the film (consecutive marked cells merge into wider marks):
░░░░░░█████░█░█░█░█░█░█░█░█░█░█░█░███░░░░░░
└────┘└───────────────────┘└──┘
entry data area exit
(6) (21 alt. pulses) (4)
The leading and trailing ░░░░░░ are unexposed film base outside the
DX code (the inter-code gap between this code and its neighbours, or
the film leader). Only the 31 cells inside the brackets are part of
this code instance.
The clock track is data-independent. It is the same on every DX code, regardless of film type or frame number. It provides:
- A wide entry mark (5 cells filled), distinguishable from any data pattern, for sync at the start of a code.
- Regular alternating marks/gaps in the data area (cells 7–27), so the reader knows where to sample the data track.
- A wide exit mark (3 cells filled), for end-of-code validation.
The clock track's regular spacing in the data area is also why DX edge codes are decodable by standard ITF (Interleaved 2-of-5) barcode readers: the alternating bar/space pattern matches ITF's expectation.
A 6-cell two-track pattern (per patent FIG. 5):
Cell: 1 2 3 4 5 6
Clock track: █ █ █ █ █ ░ (5-cell mark + trailing gap)
Data track: █ ░ █ ░ █ ░ (3 single-cell marks at 1/3/5 + trailing gap)
As it appears on film (consecutive marked cells merged):
Clock: █████░
Data: █░█░█░
The sentinel has two components, both part of the matched pattern:
- A 5-cell mark in the clock row (cells 1–5) and 3 single-cell marks
in the data row at cells 1, 3, 5 — the data row alternates
█░█░█while the clock row stays continuously filled across all 5 cells - A trailing empty column at cell 6 — both tracks gap. This position is part of the sentinel pattern; the reader checks that cell 6 is empty in both tracks when validating the match.
The trailing empty column also creates a fixed boundary between the sentinel and the start of the data area at cell 7 — without it, a PC bit of 1 at cell 7 would visually merge into the wide clock mark with no separation.
The combined 2D pattern (specific bit values across all 6 cells × 2 tracks = 12 cells) cannot be produced by any valid data combination within the data area, so the reader can identify the start of a code.
The 21-cell data area carries 21 bits, of which 18 are payload and 3 are structural (2 dividers + 1 parity bit).
| Cells | Field | Encoding | Notes |
|---|---|---|---|
| 7–13 | Part 1 (Product Code) | 7 bits MSB→LSB, weights 64→1 | identifies film manufacturer + product class |
| 14 | Divider | always 0 (gap) | gap between Part 1 and Part 2 |
| 15–18 | Part 2 (Generation Code) | 4 bits MSB→LSB, weights 8→1 | identifies variant within product class |
| 19–24 | Frame number | 6 bits MSB→LSB, weights 32→1, range 0–63 | |
| 25 | Half-frame indicator | 1 = half ("A"), 0 = whole | |
| 26 | Divider | always 0 (gap) | gap before parity |
| 27 | Parity bit | even parity over the 18 data bits |
I verified the field positions, bit order, divider locations, and parity polarity against a Kodak Portra 160 New strip decode, then cross-checked the layout against patent FIG. 5.
All multi-bit fields are encoded MSB-first in the direction of
cell-index increase. For example, the Part 1 bit at cell 7 carries
weight 64 (= 2^6), cell 8 = 32, …, cell 13 = 1.
1→ half-frame label (the "A" suffix you see on Kodak rolls:1A,2A,3A, …)0→ whole-frame label
Even parity over the 18 useful data bits: Part 1 (7) + Part 2 (4) + frame_num (6) + half-frame (1) = 18 bits.
The parity bit is set such that the count of 1s across all 19 bits (data + parity) is even. Equivalently:
parity = bit_0 XOR bit_1 XOR ... XOR bit_17
The 7-bit Part 1 + 4-bit Part 2 identify a film stock and variant
(e.g., (Part 1 = 95, Part 2 = 14) = Kodak Portra 160). They don't
directly encode anything else: no speed, no chemistry, no exposure
latitude. It is just an identifier.
This is different from the DX cartridge code. The cartridge code
(12 metal contacts on the cartridge body, read by cameras) encodes ISO
speed directly through contact patterns, so a camera can read the film's
ISO without a lookup table. The edge code does not carry ISO
information. ISO speed, chemistry, layer structure, and other film
properties have to be looked up in an external table indexed by the
(Part 1, Part 2) pair.
The official (Part 1, Part 2) → film properties mapping moved through
a few US/international standards bodies:
- NAPM (National Association of Photographic Manufacturers, US-based, ~1947 – 2002) — assigned the original Part 1 and Part 2 values to manufacturers; the patent calls the combined value the "NAPM number"
- PIMA (Photographic and Imaging Manufacturers Association, ~early 1990s – 2003, successor to NAPM) — published assignment tables under standard IT3.42 (1993)
- I3A (International Imaging Industry Association, 2003 – 2012, successor to PIMA) — published the I3A DX codes catalog (the 2008 edition is widely cited by community projects)
- IS&T (Society for Imaging Science & Technology, imaging.org; the organisation itself was founded 1947 but its DX-catalog work is more recent: a 2011 – 2014 IS&T catalog is referenced by community projects like thebigfilmdatabase.merinorus.com)
After IS&T's 2011–2014 catalog, central maintenance of the DX assignment
table appears to have wound down. Films launched after that window often
have (Part 1, Part 2) values that are not in widely published tables.
They are usually documented through community reverse-engineering
instead.
For films launched after the last published catalog, manufacturers kept
producing new emulsions but no longer had an obvious central authority
allocating fresh (Part 1, Part 2) pairs. In practice, they seem to
self-allocate within existing Part 1 ranges.
Modern Kodak Portra 160 (launched 2011) is one example. Community
decodes report (Part 1 = 95, Part 2 = 14), consistent with empirical
mark reads. This is not an authoritative assignment: modern Portra 160
is not listed in the older PIMA tables, and a strict lookup against
those tables returns "unknown." Part 1 = 95 is historically assigned to
Kodak in the older tables, so the Part 1 byte still identifies it as
Kodak 35mm color negative film. I have not confirmed whether Part 2 =
14 was officially allocated by I3A or IS&T; it could be a late-catalog
assignment or a Kodak self-allocation within the Part 1 = 95 range.
A modern DX edge code decoder should expect (Part 1, Part 2)
combinations that are not in any known table. I would handle them like
this:
- Look up the full pair in the historical PIMA table first.
- If the pair isn't found but Part 1 is known, fall back to the Part-1-level information (manufacturer + product class) and treat Part 2 as unknown.
- Maintain a community-augmented table for entries beyond the latest widely-published catalog — the community film database thebigfilmdatabase.merinorus.com collects modern empirical decodes with film-stock identifications.
- Do not infer ISO from Part 1 alone. Part 1 ranges loosely correlate with product class but not with ISO. Different Part 2 values within the same Part 1 have historically mapped to different ISO speeds. For modern self-allocated codes, the ISO isn't knowable without either an empirical decode or a community-database hit.
The 6-bit field can encode 0–63, but factory rolls only use specific values that correspond to physical frame positions. The visible sequence printed every 19mm along a roll edge typically looks like:
…, X, XA, 00, 00A, 0, 0A, 1, 1A, 2, 2A, …, 36, 36A, 37, 37A, …
X/XA— leader/trailer markers (common on Kodak rolls; specific bit value not yet verified against a strip)00/00A,0/0A— additional pre-frame numbers; often not used for image content because the camera advances past these positions when loading (pre-roll before the first exposure)1–36— image frames (typical 36-exposure roll)37/37A— end-of-roll marker
Each numeric label appears as a pair (whole + half), giving two DX
codes per 38mm image-frame pitch. The non-numeric markers (X, etc.)
follow the same pair convention.
I have not verified the exact 6-bit values used for X / XA and
other letter-only markers. The 6-bit field has room for them: 0–63 is
64 distinct values, far more than the roughly 40 labels normally used.
A 4-cell two-track pattern (per patent FIG. 5):
Cell: 28 29 30 31
Clock track: ░ █ █ █ (leading gap + 3-cell mark)
Data track: ░ █ ░ █ (leading gap + 2 single-cell marks at 29/31)
As it appears on film (consecutive marked cells merged):
Clock: ░███
Data: ░█░█
Like the entry sentinel, the exit sentinel has two components, both part of the matched pattern:
- A leading empty column at cell 28 — both tracks gap. This position is part of the sentinel pattern; the reader checks that cell 28 is empty in both tracks when validating the match.
- A 3-cell mark in the clock row (cells 29–31) and 2 single-cell
marks in the data row at cells 29 and 31 — the data row alternates
█░█while the clock row stays continuously filled across all 3 cells.
Like the entry's trailing empty column, the leading empty column creates a fixed boundary between the parity bit (cell 27, last cell of the data area) and the wide exit clock mark. Without it, a parity bit of 1 at cell 27 would visually merge into the exit clock mark.
The combined 2D pattern is unique within the 31-cell code, so it also validates the end of the code.
Entry and exit sentinels use the same basic structure:
- Both have a wide continuous mark in the clock row paired with a shorter alternating pattern in the data row, plus an empty column adjacent to the data area
- Entry sentinel (6 cells): 5-cell clock-row mark + 3 alternating
marks in the data row (
█░█░█on cells 1–5) + trailing empty column on both rows at cell 6 - Exit sentinel (4 cells): leading empty column on both rows at
cell 28 + 3-cell clock-row mark + 2 alternating marks in the data
row (
█░█on cells 29–31) - The empty columns at the data-area boundaries (cells 6 and 28) are part of the matched sentinel patterns, not independent dividers
- Asymmetric width difference (5+3 vs 3+2) disambiguates code direction
The pattern repeats every 19mm:
entry … data … exit gap entry … data … exit gap …
The visible wide clock-track marks are useful when inspecting scans:
- Finding code locations on flatbed scans without running a decoder: look for the regularly spaced wide marks in the clock row
- Checking decoder column alignment — the wide marks should appear at expected spacing along the strip
- Detecting L-R flipped scans — both sentinels feature a continuous clock-row mark with an alternating data-row pattern below, so they superficially resemble each other. Disambiguate by width (entry: 5-cell; exit: 3-cell) or by checking adjacent printed digit labels (the human-readable frame number sits in a known position relative to the marks)
The 2-track pattern gives a reader more reliable sync detection than a single-track pattern would:
- Smudge / scratch tolerance: a scratch or smudge in one track alone can't fake either sentinel — the other track's expected pattern wouldn't match.
- Concurrent validation: a reader can validate sync by checking "wide pulse on clock" and "alternating marks on data" within the same time window — a single test that both tracks pass simultaneously.
- Ambiguity rejection: random noise in either track by itself can't reach both the wide clock-mark threshold and the specific alternating-data pattern at the same time.
- Adjacent human-readable digit tolerance: every DX code is printed alongside human-readable frame-number digits (the literal characters "1", "2A", "36", etc.), placed just before each code on the film edge. A 1D-only clock-track sentinel could be falsely triggered by the vertical strokes inside those printed digits — a "1" or the spine of a "5" can resemble a wide mark pattern in a single track. Requiring the 2D combination (specific clock pattern and specific data pattern simultaneously) means the reader can't confuse the digit labels for the start of a code.
The DX edge code sits immediately below the bottom perforation row, with no margin between the perforations and the top edge of the clock track. In practice, perforation holes can slightly overlap the top of the clock track. Even a small intrusion (e.g., 10% of cell height) clips the top of any "on" cell in the clock track that happens to sit under a perforation.
The remaining cell still reads as "on" reliably under binary thresholding because it is still distinctly darker than an "off" cell. Density-based reading is more sensitive to this: an "on" cell with 10% of its top clipped reads at roughly 90% strength. Decoders that average density across the full row height will see periodic dips in clock-track signal strength matching the perforation spacing.
Mitigations:
- Sample density only from the bottom portion of the clock-track row (below where perf intrusion can reach), or
- Use binary thresholding with margin, rather than absolute density
The data track (lower of the two) is not affected by this because it sits farther from the perforations.
A typical DX edge-code reader:
- Localize the tracks: find the two parallel tracks in the image (perspective-corrected if needed).
- Detect clock track transitions: find each mark/gap edge in the clock track to establish the per-cell position grid.
- Sample data track at each cell center → 31-bit data sequence.
- Find entry sentinel at cells 1–6: clock=
111110, data=101010. If no match, slide the 31-cell window and try again (handles unknown phase / where the code starts in the image). - Extract fields from cells 7–27 using the table above (MSB→LSB).
- Validate parity at cell 27 against the 18 useful data bits (even parity).
- Validate exit sentinel at cells 28–31: clock=
0111, data=0101. - Output: tuple
(Part 1, Part 2, frame_number, half_frame), plus an optional confidence score from how cleanly the sentinels matched and parity validated.
Part 1 = 95, Part 2 = 14, frame = 5, half = 0 (whole frame).
Part 1 = 95 = 1011111 (7 bits MSB→LSB, cells 7–13)
Part 2 = 14 = 1110 (4 bits MSB→LSB, cells 15–18)
frame = 5 = 000101 (6 bits MSB→LSB, cells 19–24)
half = 0 = 0 (whole frame, cell 25)
18 data bits: 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 0 1 0
Count of 1s = 11 → odd → parity bit must be 1 to make total even.
Full data track (cells 1–31):
1 0 1 0 1 0 | 1 0 1 1 1 1 1 | 0 | 1 1 1 0 | 0 0 0 1 0 1 | 0 | 0 | 1 | 0 1 0 1
─ entry ── ─ Part1 = 95 ─ d Part2=14 ── frame=5 h d p exit
Full clock track (cells 1–31, identical for every code):
1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1
─ entry ── ── data area: alternating clock pulses ── ─ exit ──
- Tolerance specifications (mark width tolerance, contrast minimums, acceptable skew angle, etc.) — these matter for reader design, but are outside this format reference.
- Manufacturer-specific deviations — Kodak, Fuji, Agfa, Ferrania all use this same standard, but film-stock-specific quirks (latent image density, dye color, latent fade) affect reader design more than format interpretation.
- Backwards readability — the asymmetric entry vs. exit sentinels (5-cell vs. 3-cell clock marks) make the format directional; a reader reading in the wrong direction would fail the entry-sentinel match and need to flip its sample buffer.
- Part 1 / Part 2 → ISO speed mapping — separate concern; see the lookup tables section above, along with community archives like thebigfilmdatabase.merinorus.com.
- Recent film assignments — central catalog maintenance moved through PIMA → I3A (2008 catalog) → IS&T (2011–2014 catalog) and appears to have wound down after that. Films launched after the latest widely-published catalog (e.g., modern Portra 160 with Part 1 = 95, Part 2 = 14) may use either late-catalog official assignments or manufacturer self-allocations within existing Part 1 ranges. They tend to end up documented in community databases and reverse-engineering work.
- Original patent: Oliver, Johnson, Patton (Eastman Kodak), US 4,965,628, "Photographic Film with Latent Image Multi-Field Bar Code and Eye-Readable Symbols," issued Oct. 23, 1990. The patent's FIG. 5 shows the complete 31-cell clock track pattern and the individual field labels (clock entry, film product class, film specifier, frame no., 1/2 frame flag, parity, clock exit, spares).
- Open-source decoder implementation: zxing-cpp's
ODDXFilmEdgeReader.cpp— useful if you are implementing a reader. - Formal standards (apparently paywalled; not directly consulted
for this doc):
- ISO 1007 "Photography — 135-size and Type 135 magazines, films and cassettes — Specifications" — sold by ISO at iso.org/standard/5512.html. The standard supersedes the earlier US national lineage and is the formal authority for the format today.
- US national lineage referenced in the patent body and in Wikipedia: ANSI PH1.14 → NAPM IT1.14 (1994) → PIMA IT3.42. Sold by ANSI at webstore.ansi.org. The exact standard number cited varies between sources; the patent's body text specifically names "PIMA Standard IT3.42 and ISO Standard 1007."
- PIMA / I3A / IS&T DX assignment tables — the lookup that maps
(Part 1, Part 2)→ film stock, distinct from the format spec itself. See Lookup tables. - Community film database: thebigfilmdatabase.merinorus.com
- Alternative names for Part 1 / Part 2. Part 1 is sometimes called
the Product Code (or
ProdCodein code/identifier form), and Part 2 is sometimes called the Generation Code (orGenCode). These names show up in software that reads DX codes — for example, the Pakon F-135+ minilab scanner software usesProdCodeandGenCodeas the internal field names. The bit positions and encoding are unchanged; only the labels differ. The patent's body text uses yet another pair of names: "product code" and "generator number". See "Terminology in the patent" below. - Terminology in the patent. The 1990 Kodak patent (US 4,965,628)
refers to the two fields as "product code" (Part 1) and "generator
number" (Part 2) in its body prose, while FIG. 5 labels them as
"FILM PRODUCT CLASS" and "FILM SPECIFIER" respectively. The combined
(Part 1, Part 2)tuple is called the "NAPM number" in the patent, reflecting that NAPM was the original allocator.