Revvity/PerkinElmer Harmony exports (Opera Phenix, Operetta)
Harmony, the software of the Opera Phenix and Operetta high-content screening systems, exports a measurement as an XML index plus one TIFF per plane. OpenReadout reads the index and returns the plate with one image per field of view, each with the plate’s channels, Z planes and time points. Columbus exports and standalone Opera .flex files are read too. The shared plate model is described in hcs.md.
Derived from three public Harmony indexes (V4 from an Operetta, V5 from a Phenix and a Sonata). The results were checked against the same indexes parsed with the Python standard library, against tifffile on every plane file and against Bio-Formats 8.5.0 run as a black box. No Revvity/PerkinElmer document, schema or software was used; every name below is ours. Format id opera-harmony, crate openreadout-hcs (module harmony). Provenance: docs/provenance/opera-harmony.md.
A Harmony export is a measurement folder (<plate>__<date>-Measurement<n>/) whose Images/ folder holds Index.idx.xml (Harmony 6 and 7: Index.xml) and one TIFF per plane; some exports put the index and the TIFFs directly in the plate folder. Open the measurement folder, the Images folder or the index (Index.idx.xml; Index.xml and Index.ref.xml with the same root element are accepted).
Columbus exports use the same index structure in the namespace http://www.perkinelmer.com/Columbus (ImageIndex.ColumbusIDX.xml, next to MeasurementIndex.ColumbusIDX.xml): the reader handles them too, with three differences: URL has an attribute BufferNo, the zero-based page of a multi-page TIFF or Opera .flex file that holds several planes (PlaneFile.page); ChannelColor is a 32-bit ARGB number (channels[].color #RRGGBB); Well id is an internal number, not RRCC. format_version and instrument.software are Columbus.
Standalone Opera .flex files (module flex): an Opera writes one multi-page TIFF per well, <row><col><...>.flex (001002000.flex = row 1, column 2), whose first IFD carries tag 65200 (TAG_FLEX_XML): an XML document Root with Arrays/Array (one per page, in page order; @Name such as Exp1Cam1, used as the channel name) and FLEX (@version → format_version FLEX 1.8.1.0; @OperaDevice → instrument.model) holding the light sources (LightSource@ID, Wavelength nm), light-source combinations, objectives (Magnification, NumAperture, Immersion = the immersion’s refractive index → plate.extra.objective_immersion_refractive_index), sublayouts (field offsets), stacks (Z offsets), the plate (PlateName → plate type, XSize rows, YSize columns, Barcode → plate id, StartTime) and the Well (WellCoordinate@Row/@Col, 1-based) with one Images/Image@BufferNo per page: BufferNo = zero-based page, ExposureNo = channel, Stack = Z plane, Sublayout = field, CameraExposureTime (s), PositionX/Y/Z (m), DateTime, ImageResolutionX/Y (m per pixel), LightSourceCombinationRef (a single light source’s wavelength → the channel’s excitation_nm). Open a .flex file (that one well) or the measurement folder of .flex files (the plate, flex_files). Validated on an OPERA5013 (FLEX 1.8.1.0, idr0001; 64 planes equal to tifffile and to Bio-Formats) and a later Opera’s files written with a Columbus export (FLEX 1.8.1.1). Not validated: time series (Kinetic), compressed pages.
Detection
An .xml file whose first 4 KB hold the root element EvaluationInputData and the namespace segment PEHH or perkinelmer.com/Columbus (definite); a folder with such an index directly inside or in Images/; a TIFF with the .flex extension (is_flex_name), or a folder of them without a Harmony/Columbus index.
The index (XML, streamed)
| element (path) | our field | notes |
|---|---|---|
root namespace …/PEHH/HarmonyV<n> (Harmony 7: a GUID path 43B2A954-…/HarmonyV7) |
format_version (HarmonyV5), instrument.software Harmony, software_version 5 |
manufacturer PerkinElmer when the namespace names it |
User |
operator |
|
InstrumentType |
instrument.model (Operetta, Phenix, Sonata) |
|
Plates/Plate/PlateID |
plate id, sample.id, sample.barcode |
|
Plate/Name |
plate name when it differs from the id |
|
Plate/MeasurementID |
plate.extra.measurement_id |
|
Plate/MeasurementStartTime |
started_at |
|
Plate/PlateTypeName |
plate_type |
|
Plate/PlateRows, PlateColumns |
rows, columns |
the standard plate that holds every record when absent or too small |
Plate/Well id="RRCC" |
declared_wells |
selected wells; those without records are listed in plate.extra.selected_wells_without_images |
Maps/Map/Entry ChannelID/FlatfieldProfile |
info --view full only |
flat-field profile text per channel; not applied |
Maps/Map/Entry ChannelID/{ChannelName, ImageResolutionX/Y, ImageSizeX/Y, Main*Wavelength, Objective*, ExposureTime, …} |
the channel’s description, for every Image of that ChannelID that does not carry the field itself |
Harmony 6/7 Index.xml keeps these only here (in whichever Map holds them: the 2nd or 3rd in the corpus); an Image’s own value wins; a note says when they were used |
Images/Image |
one plane record | fields below |
Image/URL (Columbus: @BufferNo = page) |
PlaneFile.name, PlaneFile.page |
empty → not_recorded; absolute paths and URLs keep their last component |
Image/Row, Col |
well (1-based) | |
Image/FieldID |
field |
|
Image/PlaneID |
Z (sorted) | |
Image/TimepointID |
T (sorted) | |
Image/ChannelID |
C (sorted) | plate.extra.channels[].channel_id |
Image/ChannelName |
channels[].name |
from the channel’s first record |
Image/ChannelType |
channels[].acquisition_mode |
Fluorescence, Brightfield |
Image/AcquisitionType, IlluminationType, ImageType, BinningX, CameraType, MaxIntensity |
plate.extra.channels[]: acquisition_type, illumination_type, image_type, binning, camera, max_intensity |
CameraType is also instrument.detector |
Image/MainExcitationWavelength, MainEmissionWavelength |
excitation_nm, emission_nm |
0 (brightfield emission) → absent |
Image/ExposureTime (Unit="s") |
exposure_ms |
per channel and per plane (frames) |
Image/ImageResolutionX/Y (Unit="m") |
physical_size.x/y (µm) |
|
Image/ImageSizeX/Y |
size_x, size_y |
confirmed by a plane file header |
Image/PositionX/Y (Unit="m") |
extra.position_x_um/y_um, frames[].stage_x_um/y_um |
offset from the well centre (inferred: repeats across wells) |
Image/PositionZ |
frames[].stage_z_um; Z step = difference of the first two planes |
|
Image/AbsTime |
frames[].acquired_at; field acquired_at = earliest; ended_at = latest |
|
Image/MeasurementTimeOffset |
time_increment_s (difference of the first two time points) |
|
Image/ObjectiveMagnification, ObjectiveNA |
objective.nominal_magnification, lens_na |
|
Image/FlimID |
not a dimension | a note when several values occur |
Image/State, OrientationMatrix, AbsPositionZ |
info --view full only |
Plane files are named r<RR>c<CC>f<FF>p<PP>-ch<C>sk<T>fk1fl1.tiff; files of that shape in the folder that the index does not name are reported (unindexed_plane_files).
Vocabulary (module harmony)
| our name | meaning |
|---|---|
HARMONY_FORMAT_ID |
opera-harmony |
HARMONY_INDEX_NAMES |
Index.idx.xml, Index.xml, Index.ref.xml, ImageIndex.ColumbusIDX.xml (search order in a folder) |
looks_like_harmony |
detection on the first bytes |
find_index |
the index of a measurement folder (itself or its Images/) |
parse |
stream the index into an HcsPlate |
is_plane_name |
the r..c..f..p..-ch.. file-name shape |
TAG_FLEX_XML, is_flex_name, flex_files, flex_xml (module flex) |
tag 65200; a .flex file name; the .flex files of a folder; the XML of a file |
Validation (2026-09-24)
| corpus id | plate | ours vs oracle |
|---|---|---|
hcs-harmony-zenodo7841360-index |
V5 Sonata, 1 well, 1 field, 1 channel | 1 plane bit-exact vs tifffile; Bio-Formats 1/1 |
hcs-harmony-idr0034-index |
V4 Operetta, 72 wells, 647 fields x 4 channels; partial copy | 26 planes bit-exact; 2,543 missing files reported (exit 5); 19 not-recorded planes blank; Bio-Formats 26/26 planes equal (it lists 648 series: one grid position has no record in the index) |
hcs-columbus-zenodo6327496-tif-index |
Columbus, 6-well plate, 2 wells x 1 field x 3 Z x 2 channels, 6-page LZW TIFF per well | 12 planes bit-exact vs tifffile (page = BufferNo); Bio-Formats 12/12 |
hcs-columbus-zenodo6327496-flex-index |
the same plate with Opera .flex files |
12 planes bit-exact vs tifffile; Bio-Formats 12/12; OME-Zarr plate export valid NGFF 0.5 (ome-zarr-models), 12/12 planes equal |
hcs-harmony-jump-br00117035-index |
V5 Phenix, 384 wells x 9 fields x 8 channels (47 MB index); partial copy | 16 planes bit-exact; 27,632 missing reported; Bio-Formats 16/16, 3,456 series mapped |
Channel names, plate geometry, pixel sizes and the well/field of every image agree with the stdlib parse and Bio-Formats’ OME Plate. Performance: see book/src/project/performance.md (HCS row).
How this reader was derived, file by file: provenance log.