Commit 735871c
feat(dicomstudio): J2K Compare encode/decode mode pickers + Kakadu/Grok adapters
Adds explicit encode + decode API selection to the J2K Compare panel so the
panel can directly measure J2KSwift's CPU `decode` vs `decodeGPU` vs
`decodeWithGPUHT` paths (and CPU `encode` vs `encodeGPU`) on a chosen
transfer syntax, alongside OpenJPEG and (when installed) Kakadu / Grok CLI
decoders. Timing methodology mirrors J2KSwift's published
`CROSS_HOST_*_inproc.md` reports: a single reused encoder/decoder per
fixture, 2 untimed warmups + 7 timed runs, median of 7, `DispatchTime`
nanosecond clock.
DICOMCore adds:
* `J2KSwiftDecodeMode` / `J2KSwiftEncodeMode` enums (`.cpu`, `.decodeGPU`,
`.decodeWithGPUHT` / `.cpu`, `.gpu`) — public so DICOMStudio can build a
Picker over them.
* `J2KSwiftCodec.decode(_:descriptor:mode:)` and
`J2KSwiftCodec.encode(_:descriptor:transferSyntaxUID:configuration:mode:)`
— explicit-mode entry points that bypass the runtime router used by
`decodeFrame` / `encodeFrame`.
* `J2KSwiftCodec.benchEncode` / `benchDecode` — hold one
`J2KEncoder`/`J2KDecoder` across all warmups + timed iterations,
preventing the `HTBlockEncoderConformant.useNEONHotPath` `dispatch_once`
race that crashed J2KSwift's parallel codeblock workers when a fresh
encoder was constructed per iteration on HTJ2K targets.
* `KakaduCLICodec` / `GrokCLICodec` — decode-only adapters that shell out
to `kdu_expand` / `grk_decompress`. macOS-only; locate the binary on
`$PATH` + `/usr/local/bin` + `/opt/homebrew/bin`. Both write the
encoded J2K to a temp file, run the binary to `.rawl`, read it back.
Requires sandbox-disabled builds to actually launch the subprocess
(the default Release entitlements keep sandbox on; the adapters
silently fail at runtime in sandboxed builds, leaving empty rows).
* `CLICodecSupport` — shared `locateBinary`, `runProcess`, `TempWorkDir`,
`CLICodecError`.
DICOMStudio J2K Compare panel:
* Encode-mode picker (CPU / GPU) and decode-mode picker (CPU /
decodeGPU / decodeWithGPUHT) above the comparison table; the J2KSwift
row's encode and decode columns reflect the picked APIs.
* New "Encode" column showing the J2KSwift encode time; non-J2KSwift
rows show "—" (they only decode whatever J2KSwift produced).
* Picker selections drive `benchEncode` / `benchDecode` (when warm-up is
on); cold-shot path uses a single timed iteration.
* Route badge per row shows the API actually exercised (e.g. `CPU
\`decode\``, `\`decodeGPU\``).
DICOMStudio image viewer:
* New File row at the top of the metadata overlay showing the full path
of the currently-reviewed image. Middle-truncated, selectable for
copy, full path in tooltip.
Tests (`Tests/DICOMCoreTests/`):
* `KakaduJ2KSwiftSampleStudiesTests` — cross-codec bit-exactness
validation: encodes a corpus of CT/MR/XA/PX/DX/MG fixtures with
J2KSwift, decodes back with both J2KSwift (per-mode) and Kakadu,
asserts byte-equality across all paths, prints a per-fixture timing
report (2+7 median methodology). Skips cleanly when
`KakaduCLICodec.binaryPath` is nil or `SampleStudies/` is absent
(developer-only fixtures, not in repo).
* `DICOMStudioPanelSubstituteTests` — multi-mode driver that exercises
the same `benchEncode` / `benchDecode` APIs the panel uses, without
launching the SwiftUI app. Same skip behaviour as above.
Notes:
* Does not bump `Package.swift` — J2KSwift v8.0+ ships a `package(path:
"../CompressionFamily")` in its manifest that blocks SwiftPM URL
consumption from a third-party repo. Stays on `from: "5.21.0"` which
resolves to v5.22.0; everything in this PR compiles against that.
* Does not change `DICOMStudio.entitlements` — sandbox stays on in
Release. The Kakadu/Grok CLI rows in the J2K Compare panel require a
sandbox-disabled dev build to actually run.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>1 parent f870e09 commit 735871c
9 files changed
Lines changed: 1346 additions & 85 deletions
File tree
- Sources
- DICOMCore
- DICOMStudio
- ViewModels
- Views
- Tests/DICOMCoreTests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
0 commit comments