Skip to content

refactor: gitlab to github migration#4

Open
TomasBalak wants to merge 4 commits into
masterfrom
refactor/gitlab-to-github-migration
Open

refactor: gitlab to github migration#4
TomasBalak wants to merge 4 commits into
masterfrom
refactor/gitlab-to-github-migration

Conversation

@TomasBalak

@TomasBalak TomasBalak commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR migrates the CI/CD pipeline from GitLab to GitHub and applies necessary
linting fixes to ensure the merge pipeline passes. It also includes AI
review-suggested fixes for pre-existing code in master (suggestions originate from #2).

Note

This PR is a part of a two-stage PR based on #2. While there might be some issues in this PR, they should be fixed after the PR #5 is merged. The original PR was split in order to be more "review-friendly."

Changes

CI/CD Migration

  • Migrate from GitLab CI to GitHub Actions

Build System & Config

  • Migrate from pdm to uv
  • Update tooling

Documentation

  • Update instructions to be consistent with the migrated repository
  • Fix formatting and linting issues in docs/getting-started/quality-control.md

Linting & AI Review Fixes (Existing Code)

Summary by CodeRabbit

  • Documentation

    • Expanded installation and quality control guides with detailed function examples and workflows
    • Added step-by-step development environment setup instructions to README
  • Chores

    • Bumped version to 1.0.1
    • Migrated project build system from PDM to UV
    • Updated CI/CD configurations and linting tools

- Replace GitLab CI with GitHub Actions workflows
- Migrate from pdm to uv
- Update pre-commit config, ruff, mypy, and markdownlint configs
- Update documentation URLs from GitLab to GitHub
- Reorganize pyproject.toml for uv compatibility
- Fix import ordering across all modules
- Fix folding threshold calculation for empty masks
- Apply ruff auto-fixes for consistency
@TomasBalak TomasBalak self-assigned this Jun 9, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the project's development environment and dependency management from PDM to uv, updates pre-commit and linter configurations, and revises the documentation and example code. It also refactors the threshold calculation logic in folding.py and standardizes imports across several modules. The review feedback highlights several critical issues in the updated documentation where incorrect dictionary keys are used (which would cause KeyErrors at runtime), suggests falling back to the tile's own tissue mask in folding.py when the local neighborhood is empty, recommends raising a ValueError for mismatched local parameters, and points out redundant imports in blur_score_laplacian.py.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread docs/getting-started/quality-control.md
Comment thread docs/getting-started/quality-control.md
Comment on lines +32 to +42
if local_values.size > 0:
return threshold_yen(local_values)
else:
return 1.0

values = MaskedArray(img, ~mask).compressed()

if values.size == 0:
return 1.0

return threshold_yen(values)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If local_tiles is provided but the local neighborhood has no tissue (i.e., local_values.size == 0), the function currently returns a hardcoded 1.0 threshold. This completely ignores the current tile's own tissue mask (img and mask), which could still contain valid tissue. Instead, it should fall back to the tile's own tissue mask to calculate the threshold, and only return 1.0 if the tile's own tissue mask is also empty.

Suggested change
if local_values.size > 0:
return threshold_yen(local_values)
else:
return 1.0
values = MaskedArray(img, ~mask).compressed()
if values.size == 0:
return 1.0
return threshold_yen(values)
if local_values.size > 0:
return threshold_yen(local_values)
values = MaskedArray(img, ~mask).compressed()
if values.size == 0:
return 1.0
return threshold_yen(values)

Comment on lines +29 to +30
if local_tiles is not None and local_mask is not None:
local_values = MaskedArray(local_tiles, ~local_mask).compressed()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If only one of local_tiles or local_mask is provided, the function silently ignores local_tiles and falls back to the global image. It is safer and more robust to raise a ValueError when there is a mismatch between these two parameters.

    if (local_tiles is None) != (local_mask is None):
        raise ValueError("Both local_tiles and local_mask must be provided together.")

    if local_tiles is not None and local_mask is not None:
        local_values = MaskedArray(local_tiles, ~local_mask).compressed()

Comment thread rationai/qc/blur/blur_score_laplacian.py Outdated
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR migrates the project from PDM to UV as the dependency manager, updates CI/CD infrastructure to use GitHub Actions with shared templates, refines pre-commit and linting configurations, and comprehensively updates documentation and examples. Minor code improvements enhance type safety in the folding module and consolidate imports across QC functions. Version bumped to 1.0.1.

Changes

Build System and Tooling Migration

Layer / File(s) Summary
GitHub Actions workflow delegation
.github/workflows/mkdocs-build.yml, .github/workflows/python-lint.yml
New GitHub Actions workflows delegate MkDocs builds and Python linting to shared RationAI organization templates, replacing GitLab CI configuration.
Pre-commit hooks and Ruff configuration
.pre-commit-config.yaml, .ruff.toml
Commitizen hook and Ruff are bumped to newer versions; Ruff linting switches from PDM-wrapped pdm lint to native ruff-check with auto-fix enabled. Lint rule selection expanded to include type-checking (TC) and import organization (TID, UP). The piqe.py module is excluded from Ruff checks.
Type checking and markdown linting configuration
.markdownlint.yaml, .mypy.ini
Markdownlint rules are disabled to permit flexible documentation formatting. Mypy is configured to skip type-checking for the external piqe module and enforce explicit package bases.
Project version and dependency groups
pyproject.toml
Version bumped to 1.0.1 with updated author metadata. Git-based dependencies are reordered, and PDM-specific dev dependency configuration is replaced with PEP 735 dependency-groups defining dev, test, and docs groups.

Documentation, Examples, and Code Quality

Layer / File(s) Summary
Developer setup and installation documentation
README.md, docs/index.md, docs/getting-started/installation.md
README replaces TODO placeholder with documentation links and step-by-step dev environment setup. Installation guide is updated to use UV for package installation from the GitHub repository instead of PDM.
Quality Control function examples
docs/getting-started/quality-control.md
Residual artifacts example now demonstrates StandardConversions.RGB2HER and artifact mask saving. Blur scoring example is expanded with explicit pixel_size, tissue mask computation, and clarification of the two returned masks. Folding example is rewritten to show local area handling, tissue mask computation for both global and local images, and output mask generation.
Import consolidation and folding threshold logic
rationai/qc/blur/blur_score_laplacian.py, rationai/qc/residual_artifacts/residual_artifacts_and_coverage.py, rationai/qc/staining/*.py, rationai/qc/folding/folding.py
Staining-related imports are consolidated and reordered for consistency across modules. The _get_threshold helper in folding now uses stricter parameter types (NDArray[np.float64], BinaryMask) and improved logic: local thresholding only when both local tiles and mask are provided, returning 1.0 for empty value sets instead of always invoking threshold_yen.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

📦✨ From PDM to UV we hop,
Workflows flow through RationAI's shop,
Examples bloom with clearer light,
Thresholds refined, imports tight,
One-point-oh-one takes flight! 🐰🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: gitlab to github migration' directly and accurately summarizes the main objective of this PR, which is to migrate the repository's CI/CD infrastructure from GitLab to GitHub and update associated build tooling and configurations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/gitlab-to-github-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@TomasBalak TomasBalak changed the title refactor: gitlab-to-github-migration refactor: gitlab to github migration Jun 9, 2026
Both ColorConversion and convert_color are now imported from
rationai.staining, consistent with the rest of the codebase.
@TomasBalak TomasBalak marked this pull request as ready for review June 9, 2026 18:55
@TomasBalak TomasBalak requested review from a team June 9, 2026 18:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
rationai/qc/folding/folding.py (2)

119-121: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix dtype mismatch in local_eosin_channel initialization.

Line 121 assigns np.ones_like(local_tiles) to local_eosin_channel, but local_tiles is an RGB image with dtype uint8, while the parameter is typed as NDArray[np.float64] | None. The mypy error confirms this type mismatch.

🔧 Proposed fix
     else:
         eosin_channel = np.ones_like(tissue_mask)
         if local_tiles is not None:
-            local_eosin_channel = np.ones_like(local_tiles)
+            local_eosin_channel = np.ones(local_tiles.shape[:2], dtype=np.float64)

Note: Use shape[:2] to get height×width only, since eosin_channel is 2D while local_tiles is 3D (RGB).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rationai/qc/folding/folding.py` around lines 119 - 121, The
local_eosin_channel is created from local_tiles which is an RGB uint8 array
causing a dtype mismatch; instead allocate a 2D float array matching the spatial
shape of local_tiles (height×width) and dtype float64 like
eosin_channel/tissue_mask. Replace the np.ones_like(local_tiles) usage by
creating an array using local_tiles.shape[:2] (or np.ones_like(tissue_mask))
with dtype=np.float64 so local_eosin_channel has the correct 2D shape and type;
update references to local_eosin_channel accordingly.

Source: Pipeline failures


154-169: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Eliminate variable redefinition and simplify control flow.

The variable result is defined with type annotation on both line 156 and line 163, causing a mypy redefinition error. Both branches also construct nearly identical dictionaries, differing only in the value of folding_test.

♻️ Proposed refactoring
     if hematoxylin_eosin_stained:
         folding_test = reconstruction(folding_test_markers, thresholded_eosin)
-        result: FoldArtifacts = {
-            "folding": folding_test,
-            "thresholded_saturation": thresholded_saturation,
-            "thresholded_eosin": thresholded_eosin,
-            "thresholded_value": thresholded_value,
-        }
-        return result
-    result: FoldArtifacts = {
+    else:
+        folding_test = folding_test_markers
+    
+    result: FoldArtifacts = {
         "folding": folding_test,
         "thresholded_saturation": thresholded_saturation,
         "thresholded_eosin": thresholded_eosin,
         "thresholded_value": thresholded_value,
     }
+    
     return result
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rationai/qc/folding/folding.py` around lines 154 - 169, The code duplicates
the FoldArtifacts dict and redefines `result` twice, causing a mypy redefinition
error; fix by computing `folding_test` conditionally (e.g., set `folding_test =
reconstruction(folding_test_markers, thresholded_eosin)` only if
`hematoxylin_eosin_stained` else keep existing value or None) and then build and
return a single `result: FoldArtifacts = { "folding": folding_test,
"thresholded_saturation": thresholded_saturation, "thresholded_eosin":
thresholded_eosin, "thresholded_value": thresholded_value }` once; update the
code paths in the folding function so only `folding_test` is chosen per branch
and the `result` construction/return is unified.

Source: Pipeline failures

🧹 Nitpick comments (3)
.github/workflows/mkdocs-build.yml (1)

11-11: Shared workflow @main reference appears intentional in this repo.

Both .github/workflows/mkdocs-build.yml and .github/workflows/python-lint.yml delegate to RationAI/.github/.github/workflows/*@main``, indicating the @main (unpinned) pattern is used consistently for “RationAI Standard” shared workflows. Supply-chain risk remains as a design tradeoff; pinning to a commit SHA should be considered only if required by your security policy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mkdocs-build.yml at line 11, The workflow reference
"RationAI/.github/.github/workflows/mkdocs-build.yml@main" is intentionally
unpinned; decide whether to leave it as `@main` or pin to a specific commit
SHA/tag for supply-chain risk reduction. If you must pin, replace the `@main`
suffix with a vetted commit SHA or release tag in the reference string (and make
the same change for the analogous
"RationAI/.github/.github/workflows/python-lint.yml@main" reference), run the CI
to validate the included shared workflows still resolve and behave as expected,
and update repository docs/policy noting the chosen pinning strategy.

Source: Linters/SAST tools

docs/getting-started/installation.md (1)

21-36: ⚡ Quick win

Consider documenting the Python version requirement.

The installation instructions work correctly, but the project requires Python >=3.11 (as declared in pyproject.toml). Adding a note about this requirement would help users avoid installation failures due to Python version mismatches.

📋 Suggested addition

Add a brief note before or after the installation methods:

!!! note "Python Version"
    This library requires Python 3.11 or higher.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/getting-started/installation.md` around lines 21 - 36, Add a short note
about the Python version requirement near the "### Install the Core Quality
Control Library" section: state that the library requires Python 3.11 or higher
(as declared in pyproject.toml) and place this note immediately before or after
the listed installation methods so users see it before attempting installation.
rationai/qc/blur/blur_score_laplacian.py (1)

5-5: 💤 Low value

Mypy stub incompleteness for skimage.morphology (informational).

The pipeline shows mypy errors for binary_dilation and binary_erosion not being explicitly exported. These functions are available in scikit-image 0.24.0 at runtime but may be missing from the type stubs. This is a mypy tooling limitation rather than a code issue.

If you want to suppress these specific mypy errors, you can add # type: ignore[attr-defined] comments, but the code itself is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rationai/qc/blur/blur_score_laplacian.py` at line 5, Mypy reports missing
attributes for skimage.morphology exports; silence this tool-only error by
annotating the import of binary_dilation and binary_erosion with a type ignore
for undefined attributes (add "# type: ignore[attr-defined]" to the import line
that brings in binary_dilation and binary_erosion) so the runtime-available
functions binary_dilation and binary_erosion can be used without failing static
checks.

Source: Pipeline failures

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/getting-started/quality-control.md`:
- Around line 238-240: The code uses the wrong dict key for the FoldArtifacts
output; change the access to use the TypedDict's defined key "folding" instead
of "folding_per_pixel" (i.e., update the expression that builds mask from
artifacts[...] so Image.fromarray receives artifacts["folding"].astype(np.uint8)
scaled appropriately); look for the mask creation line that calls
Image.fromarray and update the key reference to "folding".
- Around line 56-62: The example uses wrong dictionary keys from the
ResidualArtifacts TypedDict; replace any use of artifacts["artifacts_per_pixel"]
with artifacts["coverage_mask"] and stop referencing non-existent keys
artifacts['number_of_flagged_pixels'] and
artifacts['number_of_examined_pixels']; instead use the provided
artifacts["coverage"] value and the coverage_mask to compute or describe
coverage as needed. Update the explanatory text that mentions "examined pixels"
and "flagged pixels" to refer to the API's terms ("coverage_mask" and
"coverage") or to derived values computed from those two fields (e.g., percent
coverage) so documentation matches the ResidualArtifacts contract.

In `@pyproject.toml`:
- Around line 19-20: The pyproject.toml still lists git dependencies pointing to
GitLab for the rationai-masks and rationai-staining packages; update these
entries (the strings referencing "rationai-masks @
git+https://gitlab.ics.muni.cz/..." and "rationai-staining @
git+https://gitlab.ics.muni.cz/...") to the correct GitHub repository URLs if
those packages were migrated (or else update the PR description/docs to
explicitly state these two dependencies remain on GitLab); locate the dependency
lines in pyproject.toml and replace the GitLab URLs with the authoritative
GitHub repo addresses for RationAI/masks and RationAI/staining (or add a clear
comment in the PR/docs clarifying they intentionally remain on GitLab).

In `@rationai/qc/staining/color_difference.py`:
- Line 3: Replace the incorrect import and field accesses to match the updated
rationai-staining API: change the import in this module to import
ConversionDirection (not ConversionType) alongside ColorConversion, and update
all uses of conversion.conv_type to conversion.direction and
conversion.value[...] to use conversion.matrix or conversion.inverse as
appropriate; specifically inspect code paths that expect RGB2STAIN/STAIN2RGB and
switch logic to read conversion.direction, and pull the transformation arrays
from conversion.matrix (or the inverse matrix from conversion.inverse) instead
of conversion.value.

---

Outside diff comments:
In `@rationai/qc/folding/folding.py`:
- Around line 119-121: The local_eosin_channel is created from local_tiles which
is an RGB uint8 array causing a dtype mismatch; instead allocate a 2D float
array matching the spatial shape of local_tiles (height×width) and dtype float64
like eosin_channel/tissue_mask. Replace the np.ones_like(local_tiles) usage by
creating an array using local_tiles.shape[:2] (or np.ones_like(tissue_mask))
with dtype=np.float64 so local_eosin_channel has the correct 2D shape and type;
update references to local_eosin_channel accordingly.
- Around line 154-169: The code duplicates the FoldArtifacts dict and redefines
`result` twice, causing a mypy redefinition error; fix by computing
`folding_test` conditionally (e.g., set `folding_test =
reconstruction(folding_test_markers, thresholded_eosin)` only if
`hematoxylin_eosin_stained` else keep existing value or None) and then build and
return a single `result: FoldArtifacts = { "folding": folding_test,
"thresholded_saturation": thresholded_saturation, "thresholded_eosin":
thresholded_eosin, "thresholded_value": thresholded_value }` once; update the
code paths in the folding function so only `folding_test` is chosen per branch
and the `result` construction/return is unified.

---

Nitpick comments:
In @.github/workflows/mkdocs-build.yml:
- Line 11: The workflow reference
"RationAI/.github/.github/workflows/mkdocs-build.yml@main" is intentionally
unpinned; decide whether to leave it as `@main` or pin to a specific commit
SHA/tag for supply-chain risk reduction. If you must pin, replace the `@main`
suffix with a vetted commit SHA or release tag in the reference string (and make
the same change for the analogous
"RationAI/.github/.github/workflows/python-lint.yml@main" reference), run the CI
to validate the included shared workflows still resolve and behave as expected,
and update repository docs/policy noting the chosen pinning strategy.

In `@docs/getting-started/installation.md`:
- Around line 21-36: Add a short note about the Python version requirement near
the "### Install the Core Quality Control Library" section: state that the
library requires Python 3.11 or higher (as declared in pyproject.toml) and place
this note immediately before or after the listed installation methods so users
see it before attempting installation.

In `@rationai/qc/blur/blur_score_laplacian.py`:
- Line 5: Mypy reports missing attributes for skimage.morphology exports;
silence this tool-only error by annotating the import of binary_dilation and
binary_erosion with a type ignore for undefined attributes (add "# type:
ignore[attr-defined]" to the import line that brings in binary_dilation and
binary_erosion) so the runtime-available functions binary_dilation and
binary_erosion can be used without failing static checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ffda644f-4c55-42f0-ac37-59585255edac

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3ec66 and 5039f4b.

⛔ Files ignored due to path filters (2)
  • pdm.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/mkdocs-build.yml
  • .github/workflows/python-lint.yml
  • .gitlab-ci.yml
  • .markdownlint.yaml
  • .mypy.ini
  • .pre-commit-config.yaml
  • .ruff.toml
  • README.md
  • docs/getting-started/installation.md
  • docs/getting-started/quality-control.md
  • docs/index.md
  • pyproject.toml
  • rationai/qc/blur/blur_score_laplacian.py
  • rationai/qc/folding/folding.py
  • rationai/qc/residual_artifacts/residual_artifacts_and_coverage.py
  • rationai/qc/staining/color_difference.py
  • rationai/qc/staining/dominant_stains.py
  • rationai/qc/staining/staining_difference.py
💤 Files with no reviewable changes (1)
  • .gitlab-ci.yml

Comment on lines +56 to 62
```python linenums="18"
mask = Image.fromarray(255 * artifacts["artifacts_per_pixel"].astype(np.uint8))
mask.save("residual_mask.png")

print("Coverage Number:", artifacts["coverage"])
print(f"Number of flagged pixels: {artifacts['number_of_flagged_pixels']}")
print(f"Number of examined pixels: {artifacts['number_of_examined_pixels']}")
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Correct the dictionary keys to match the ResidualArtifacts API contract.

The ResidualArtifacts TypedDict defines only two keys: coverage_mask and coverage. The example code uses incorrect keys that will cause KeyError exceptions at runtime:

  • Line 57 uses artifacts["artifacts_per_pixel"] but should use artifacts["coverage_mask"]
  • Lines 60-61 reference artifacts['number_of_flagged_pixels'] and artifacts['number_of_examined_pixels'], but these keys don't exist in the return type

The narrative text in lines 68-69 also needs updating since it references "examined pixels" and "flagged pixels" counts that aren't part of the API.

📝 Proposed fix
-mask = Image.fromarray(255 * artifacts["artifacts_per_pixel"].astype(np.uint8))
+mask = Image.fromarray(255 * artifacts["coverage_mask"].astype(np.uint8))
 mask.save("residual_mask.png")
 
-print(f"Number of flagged pixels: {artifacts['number_of_flagged_pixels']}")
-print(f"Number of examined pixels: {artifacts['number_of_examined_pixels']}")
+print(f"Coverage: {artifacts['coverage']}")

Also update the narrative in lines 68-69:

-In our example, 17900 out of the 233744 examined pixels were labeled as artifacts,
-meaning that **little more than 7% of the image's foreground area** is covered by artifacts.
+In our example, the coverage value indicates that **little more than 7% of the image's foreground area** is covered by artifacts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/getting-started/quality-control.md` around lines 56 - 62, The example
uses wrong dictionary keys from the ResidualArtifacts TypedDict; replace any use
of artifacts["artifacts_per_pixel"] with artifacts["coverage_mask"] and stop
referencing non-existent keys artifacts['number_of_flagged_pixels'] and
artifacts['number_of_examined_pixels']; instead use the provided
artifacts["coverage"] value and the coverage_mask to compute or describe
coverage as needed. Update the explanatory text that mentions "examined pixels"
and "flagged pixels" to refer to the API's terms ("coverage_mask" and
"coverage") or to derived values computed from those two fields (e.g., percent
coverage) so documentation matches the ResidualArtifacts contract.

Comment on lines +238 to 240
```python linenums="27"
mask = Image.fromarray(255 * artifacts["folding_per_pixel"].astype(np.uint8))
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Correct the dictionary key to match the FoldArtifacts API contract.

The FoldArtifacts TypedDict defines the main output mask under the key folding, not folding_per_pixel.

📝 Proposed fix
-mask = Image.fromarray(255 * artifacts["folding_per_pixel"].astype(np.uint8))
+mask = Image.fromarray(255 * artifacts["folding"].astype(np.uint8))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```python linenums="27"
mask = Image.fromarray(255 * artifacts["folding_per_pixel"].astype(np.uint8))
```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/getting-started/quality-control.md` around lines 238 - 240, The code
uses the wrong dict key for the FoldArtifacts output; change the access to use
the TypedDict's defined key "folding" instead of "folding_per_pixel" (i.e.,
update the expression that builds mask from artifacts[...] so Image.fromarray
receives artifacts["folding"].astype(np.uint8) scaled appropriately); look for
the mask creation line that calls Image.fromarray and update the key reference
to "folding".

Comment thread pyproject.toml
Comment on lines 19 to +20
"rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git",
"rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if RationAI organization has masks and staining repositories on GitHub

gh repo view RationAI/masks --json name,url 2>/dev/null || echo "masks repo not found"
gh repo view RationAI/staining --json name,url 2>/dev/null || echo "staining repo not found"

# Also check if there are any other references to gitlab URLs in the repository
rg -n 'gitlab\.ics\.muni\.cz' --type-add 'config:*.{toml,yaml,yml,md,txt}' -t config

Repository: RationAI/quality-control

Length of output: 359


Git dependencies still point to GitLab despite the “GitLab → GitHub” migration goal (pyproject.toml 19-20).

rationai-masks and rationai-staining still reference https://gitlab.ics.muni.cz/.... If the migration is intended to cover these libraries as well, update them to the correct GitHub URLs (the assumed RationAI/masks and RationAI/staining repos aren’t found on GitHub); otherwise, adjust the PR objectives/docs to clarify these specific dependencies remain on GitLab.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` around lines 19 - 20, The pyproject.toml still lists git
dependencies pointing to GitLab for the rationai-masks and rationai-staining
packages; update these entries (the strings referencing "rationai-masks @
git+https://gitlab.ics.muni.cz/..." and "rationai-staining @
git+https://gitlab.ics.muni.cz/...") to the correct GitHub repository URLs if
those packages were migrated (or else update the PR description/docs to
explicitly state these two dependencies remain on GitLab); locate the dependency
lines in pyproject.toml and replace the GitLab URLs with the authoritative
GitHub repo addresses for RationAI/masks and RationAI/staining (or add a clear
comment in the PR/docs clarifying they intentionally remain on GitLab).

@@ -1,9 +1,9 @@
import numpy as np
from numpy.typing import NDArray
from rationai.staining import ColorConversion, ConversionType

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for ConversionType and ColorConversion definitions
rg -nP --type=py -C5 'class\s+ConversionType|class\s+ColorConversion'

# Check what attributes ColorConversion has
ast-grep --pattern $'class ColorConversion {
  $$$
}'

Repository: RationAI/quality-control

Length of output: 50


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where rationai.staining is implemented in this repo
echo "== Files mentioning rationai.staining =="
rg -n --hidden --no-ignore-vcs -S "rationai\.staining" . || true

echo
echo "== Search for ConversionType and ColorConversion definitions in repo =="
rg -n --hidden --no-ignore-vcs -S --type=py "ConversionType|ColorConversion" rationai || true
echo

# Locate the staining package root
echo "== Listing rationai/staining directory (if present) =="
ls -la rationai/staining 2>/dev/null || true
echo

# Check what the package exports
if [ -f rationai/staining/__init__.py ]; then
  echo "== rationai/staining/__init__.py =="
  cat -n rationai/staining/__init__.py
else
  echo "== No rationai/staining/__init__.py found =="
fi

echo
# Show implementation files if they exist
fd -t f "color_difference.py" rationai || true
fd -t f "*staining*" rationai || true

echo
# Inspect the imported module for actual symbols/attributes
for f in $(fd -t f "conversion*.py|*color*conversion*.py|*staining*.py" rationai/staining 2>/dev/null || true); do
  echo "== File: $f =="
  head -n 200 "$f"
done

Repository: RationAI/quality-control

Length of output: 3409


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate rationai package in this sandbox =="
python3 - <<'PY'
import rationai, inspect, os
import rationai.staining as st
print("rationai module file:", getattr(rationai, "__file__", None))
print("rationai.staining module file:", getattr(st, "__file__", None))
print("rationai.staining members (sample):", sorted([n for n in dir(st) if not n.startswith("_")])[:80])
print("Has ConversionType:", hasattr(st, "ConversionType"))
print("Has ColorConversion:", hasattr(st, "ColorConversion"))
print("ConversionType object:", getattr(st, "ConversionType", None))
print("ColorConversion object:", getattr(st, "ColorConversion", None))
PY

echo
echo "== Inspect ColorConversion and its attributes =="
python3 - <<'PY'
import inspect
from rationai.staining import ColorConversion
print("ColorConversion is:", ColorConversion)
print("ColorConversion defined at:", inspect.getsourcefile(ColorConversion))
print("ColorConversion source snippet:")
src = inspect.getsource(ColorConversion)
print("\n".join(src.splitlines()[:80]))
PY

echo
echo "== Search repo for .conv_type and .value usage on ColorConversion =="
rg -n --hidden --no-ignore-vcs "\.conv_type\b|\.value\b" rationai/qc || true

echo
echo "== Search repo for ConversionType usage =="
rg -n --hidden --no-ignore-vcs "ConversionType" rationai/qc || true

Repository: RationAI/quality-control

Length of output: 262


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Repo root files for dependency info =="
ls -ლა | head -n 200

echo
echo "== Search for rationai-staining in dependency files =="
rg -n --hidden --no-ignore-vcs "rationai[-_]?staining" -S . || true

echo
echo "== Look for pyproject/requirements =="
ls -ლა pyproject.toml requirements*.txt setup.cfg setup.py poetry.lock 2>/dev/null || true

echo
echo "== Does this repo contain any rationai/staining code locally? =="
find . -maxdepth 4 -type d -name "staining" | head -n 50
find . -maxdepth 6 -type f -path "*rationai*/staining/*" | head -n 50

echo
echo "== If any local type hints reference ConversionType/ColorConversion, show them =="
rg -n --hidden --no-ignore-vcs "ConversionType|ColorConversion" rationai/qc || true

Repository: RationAI/quality-control

Length of output: 192


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Repo root files for dependency info =="
ls -la | head -n 200

echo
echo "== Search for rationai-staining in dependency files =="
rg -n --hidden --no-ignore-vcs "rationai[-_]?staining|rationai\.staining" -S pyproject.toml requirements*.txt setup.cfg setup.py poetry.lock 2>/dev/null || true
rg -n --hidden --no-ignore-vcs "rationai[-_]?staining|rationai\.staining" -S . || true

echo
echo "== Look for pyproject/requirements =="
ls -la pyproject.toml requirements*.txt setup.cfg setup.py poetry.lock 2>/dev/null || true

echo
echo "== Does this repo contain any local rationai/staining code? =="
find . -maxdepth 4 -type d -name "staining" | head -n 50
find . -maxdepth 6 -type f -path "*rationai*/staining/*" | head -n 50

echo
echo "== Any local type hints referencing ConversionType/ColorConversion? =="
rg -n --hidden --no-ignore-vcs "ConversionType|ColorConversion" rationai/qc || true

Repository: RationAI/quality-control

Length of output: 5658


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== List docs/api/staining =="
ls -la docs/api/staining || true
echo
echo "== Search docs/api/staining for ConversionType / ColorConversion =="
rg -n --hidden --no-ignore-vcs "ConversionType|ColorConversion|conv_type|\.value\b" docs/api/staining || true

echo
echo "== Show docs/api/staining __init__ or index if present =="
ls -la docs/api/staining 2>/dev/null | head -n 50

echo
# Identify likely markdown/python files
find docs/api/staining -maxdepth 2 -type f | head -n 50

echo
echo "== Print any files containing ConversionType/ColorConversion =="
for f in $(rg -l --hidden --no-ignore-vcs "ConversionType|ColorConversion" docs/api/staining 2>/dev/null || true); do
  echo "--- $f ---"
  sed -n '1,160p' "$f"
done

echo
echo "== Inspect rationai/qc/staining/__init__.py =="
cat -n rationai/qc/staining/__init__.py 2>/dev/null || true

echo
echo "== Inspect rationai/qc/staining/color_difference.py (relevant portion) =="
sed -n '1,220p' rationai/qc/staining/color_difference.py

Repository: RationAI/quality-control

Length of output: 5881


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== pyproject.toml: locate rationai-staining dependency =="
rg -n "rationai-staining" pyproject.toml
sed -n '1,80p' pyproject.toml

echo
echo "== uv.lock: show rationai-staining entries =="
rg -n "name = \"rationai-staining\"|rationai-staining" uv.lock | head -n 80

echo
echo "== Print the rationai-staining package block from uv.lock (if any) =="
python3 - <<'PY'
import re
path="uv.lock"
text=open(path,'r',encoding='utf-8').read().splitlines()
# find lines containing 'name = "rationai-staining"'
idx=[i for i,l in enumerate(text) if 'name = "rationai-staining"' in l]
print("Found blocks at lines:", idx[:10])
for i in idx[:1]:
    start=max(0,i-20)
    end=min(len(text), i+120)
    print("\n".join(text[start:end]))
PY

Repository: RationAI/quality-control

Length of output: 9747


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Python sys.path / site-packages =="
python3 - <<'PY'
import sys, site
print("sys.version:", sys.version)
print("sys.path:")
for p in sys.path:
    print(" -", p)
print("site.getsitepackages():", site.getsitepackages())
print("site.getusersitepackages():", site.getusersitepackages())
PY

echo
echo "== Search common python site-packages for rationai-staining =="
python3 - <<'PY'
import sys, site, os
paths = []
paths += site.getsitepackages()
paths += [site.getusersitepackages()]
paths = [p for p in paths if p and os.path.isdir(p)]
for base in paths:
    print("##", base)
    # list matching directories
    for cand in ["rationai", "rationai/staining"]:
        full = os.path.join(base, cand)
        if os.path.exists(full):
            print("FOUND:", full)
PY

echo
echo "== Locate any cached checkout of staining.git (uv cache / git clones) =="
# Try to find directories/files containing the commit hash from uv.lock
commit="c48d216e910df11f3e6891aa071ebb8d04635727"
rg -n --hidden --no-ignore-vcs "$commit" -S ~ 2>/dev/null | head -n 50 || true

echo
echo "== Search for ColorConversion / ConversionType symbols in site-packages and uv cache =="
python3 - <<'PY'
import os, site, sys, glob, subprocess, textwrap

bases=[]
import site as s
bases += [p for p in s.getsitepackages() if os.path.isdir(p)]
bases += [s.getusersitepackages()] if os.path.isdir(s.getusersitepackages()) else []
# also add uv cache locations
candidates=[
    os.path.expanduser("~/.cache/uv"),
    os.path.expanduser("~/.local/share/uv"),
    os.path.expanduser("~/.uv"),
]
for c in candidates:
    if os.path.isdir(c):
        bases.append(c)

# de-dupe
seen=set(); out=[]
for b in bases:
    if b not in seen:
        seen.add(b); out.append(b)

print("Search bases:")
for b in out:
    print(" -", b)

# rg is faster; run via shell for each base with tight scope
PY

# Use rg across the discovered bases only (best-effort)
# If rg finds nothing, exit 0 due to `|| true`.
for base in "$(python3 - <<'PY'
import os, site
bases=[]
import site as s
bases += [p for p in s.getsitepackages() if os.path.isdir(p)]
bases += [s.getusersitepackages()] if os.path.isdir(s.getusersitepackages()) else []
for c in [os.path.expanduser("~/.cache/uv"), os.path.expanduser("~/.local/share/uv"), os.path.expanduser("~/.uv")]:
    if os.path.isdir(c):
        bases.append(c)
# print bases one per line
for b in bases:
    print(b)
PY
)"; do
  if [ -d "$base" ]; then
    echo "## Searching in $base"
    rg -n --hidden --no-ignore-vcs --type=py "class\s+ColorConversion|class\s+ConversionType|conv_type|\.value\b" "$base" 2>/dev/null | head -n 80 || true
  fi
done

echo
echo "== Also search repository-wide for any vendored staining implementation =="
rg -n --hidden --no-ignore-vcs --type=py "class\s+ColorConversion|class\s+ConversionType" . || true

Repository: RationAI/quality-control

Length of output: 1204


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find uv caches that might contain the staining.git checkout =="
for d in \
  "$HOME/.cache/uv" \
  "$HOME/.local/share/uv" \
  "$HOME/.uv" \
  /tmp/uv* \
; do
  if [ -d "$d" ]; then
    echo "## $d"
    find "$d" -maxdepth 6 -type f -iname "*staining*" 2>/dev/null | head -n 50 || true
    find "$d" -maxdepth 6 -type d -iname "*staining*" 2>/dev/null | head -n 50 || true
  fi
done

echo
echo "== Search entire home for the pinned commit hash (best-effort, limited depth) =="
commit="c48d216e910df11f3e6891aa071ebb8d04635727"
for d in "$HOME/.cache/uv" "$HOME/.local/share/uv" "$HOME/.uv"; do
  if [ -d "$d" ]; then
    rg -n --hidden --no-ignore-vcs "$commit" "$d" 2>/dev/null | head -n 20 || true
  fi
done

echo
echo "== Search for a python module directory named rationai/staining =="
for base in /usr/local/lib/python3.11/dist-packages /usr/lib/python3/dist-packages /usr/local/lib/python3.11/site-packages "$HOME/.local/lib/python3.11/site-packages"; do
  if [ -d "$base" ]; then
    ls -la "$base/rationai" 2>/dev/null || true
    ls -la "$base/rationai/staining" 2>/dev/null || true
  fi
done

echo
echo "== If any rationai-staining source exists, print candidate file paths containing ColorConversion =="
# Find any py files that mention ColorConversion within likely cache locations
candidates=()
for d in "$HOME/.cache/uv" "$HOME/.local/share/uv" "$HOME/.uv"; do
  [ -d "$d" ] && candidates+=("$d")
done
if [ ${`#candidates`[@]} -eq 0 ]; then
  echo "No uv cache directories found."
  exit 0
fi

rg -n --hidden --no-ignore-vcs "class\s+ColorConversion|ColorConversion\s*=" "${candidates[@]}" 2>/dev/null | head -n 40 || true

Repository: RationAI/quality-control

Length of output: 416


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in docs/api/staining/*.md; do
  echo "===== $f ====="
  cat -n "$f"
  echo
done

Repository: RationAI/quality-control

Length of output: 452


🌐 Web query:

rationai-staining ColorConversion conv_type value ConversionType

💡 Result:

Based on the available documentation for the RationAI project (specifically the ratiopath library), the term ColorConversion and the parameter conv_type are not standard identifiers within the core RationAI staining API [1][2]. While RationAI provides tools for digital pathology stain conversions—such as handling stain matrices and augmentation [1][2]—the specific key-value pair structure conv_type: ConversionType appears to be characteristic of unrelated hardware or generic computer vision SDKs, such as the SIMA.ai CVU ColorConvert graph, which explicitly defines a conv_type parameter to specify color conversion operations (e.g., BGR to RGB, RGB to Gray) [3]. If you are encountering this parameter in a digital pathology workflow, it is likely part of a lower-level hardware abstraction layer or an external library integrated into the processing pipeline rather than the RationAI staining library itself. To determine the correct ConversionType values, you should consult the specific API documentation of the hardware acceleration layer or the imaging library (such as SIMA or a similar vision framework) being utilized in your code [3].

Citations:


🌐 Web query:

site:rationai.gitlab-pages.ics.muni.cz staining ColorConversion

💡 Result:

In the context of digital pathology and image processing, staining color conversion refers to the computational transformation of image data between different color spaces, specifically between standard RGB (Red, Green, Blue) color space and a stain-specific color space [1]. This process is critical for analyzing histological slides, where tissues are stained with specific dyes (such as Hematoxylin and Eosin, or H&E) to highlight different biological structures. Because digital images are typically captured in RGB, researchers use color conversion algorithms to: 1. Deconvolve or separate the signals of individual stains (Stain Deconvolution): This allows for the independent analysis of each stain component, which is essential for quantifying tissue properties or identifying specific cell types [1]. 2. Normalize staining variations (Stain Normalization): Because staining intensity can vary between labs and protocols, color conversion is used to map an image to a "standard" stain appearance, ensuring consistency for downstream machine learning or computer vision tasks [1]. These operations are often provided as specialized software libraries designed for digital pathology workflows, which facilitate the mathematical mapping between raw pixel values in RGB and the underlying stain density concentrations [1].

Citations:


🌐 Web query:

site:rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining ConversionType

💡 Result:

In the RationAI Staining docs there doesn’t appear to be a ConversionType. The API uses ConversionDirection to specify the direction of a ColorConversion, with the two directions shown in the docs/source:

  • ConversionDirection.RGB2STAIN
  • ConversionDirection.STAIN2RGB (and you can use conversion.inverse to flip direction) [1]

Source searched: the Staining “Color Conversion” API page (class ColorConversion) [1].

[1] https://rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining/api/color-conversion/


🌐 Web query:

site:rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining ColorConversion

💡 Result:

ColorConversion (in the rationai.staining library) is a class representing a color conversion between stain space and RGB space. It stores a conversion matrix and conversion direction, exposes:

  • matrix (numpy array form of the conversion matrix)
  • direction (conversion direction)
  • inverse (returns the inverse conversion, using an inverted matrix and inverted direction)
  • from_stain_vectors(stain1, stain2, stain3=None, conversion_direction=RGB2STAIN) (builds a conversion from stain vectors assumed to be in stain space; if stain3 isn’t provided it’s computed as a residual orthogonal vector; returns either STAIN→RGB or RGB→STAIN depending on conversion_direction). [1]

It’s used, for example, by AugmentStainingTransform to convert RGB→stain space, apply augmentation in stain space, then convert back. [2]

Sources: [1] (rationai.gitlab-pages.ics.muni.cz), [2] (rationai.gitlab-pages.ics.muni.cz)

Citations:


🌐 Web query:

site:rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining "conv_type"

💡 Result:

No matches for the string conv_type appear in the RationAI Staining documentation under .../digital-pathology/libraries/staining/ (it’s not referenced in the API pages that define the color/stain conversion interfaces).

Instead of a conv_type parameter, the API uses a conversion: ColorConversion object (e.g., convert_color(tile, conversion, ...)) to control what conversion is performed. [1] The stain-normalization API similarly takes explicit stain matrices/vectors (e.g., rgb2stain, target_stain1, target_stain2, …), not a conv_type selector. [2]

Sources:
[1] (rationai.gitlab-pages.ics.muni.cz)
[2] (rationai.gitlab-pages.ics.muni.cz)

Citations:


🌐 Web query:

site:rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining "ConversionType"

💡 Result:

No matches for ConversionType exist in the Staining library documentation on rationai.gitlab-pages.ics.muni.cz/digital-pathology/libraries/staining.

The API uses ConversionDirection (e.g., RGB2STAIN, STAIN2RGB) as the type controlling direction, including the ColorConversion constructor argument conversion_direction: ConversionDirection and related logic. [1]

Sources:
[1] (rationai.gitlab-pages.ics.muni.cz)

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Repo-wide usage of ConversionType =="
rg -n --hidden --no-ignore-vcs "ConversionType" rationai/qc || true

echo
echo "== Repo-wide usage of .conv_type =="
rg -n --hidden --no-ignore-vcs "\.conv_type\b" rationai/qc || true

echo
echo "== Repo-wide usage of .value\b =="
rg -n --hidden --no-ignore-vcs "\.value\b" rationai/qc || true

Repository: RationAI/quality-control

Length of output: 597


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n --hidden --no-ignore-vcs "ConversionDirection" rationai/qc || true
rg -n --hidden --no-ignore-vcs "\.direction\b" rationai/qc || true
rg -n --hidden --no-ignore-vcs "\.matrix\b" rationai/qc || true

Repository: RationAI/quality-control

Length of output: 50


Align rationai/qc/staining/color_difference.py with rationai-staining’s current ColorConversion API (lines 3, 16-19)

  • rationai-staining exports ConversionDirection (e.g., RGB2STAIN/STAIN2RGB), not ConversionType—so from rationai.staining import ... ConversionType should be replaced.
  • ColorConversion uses direction (and matrix/inverse), not conv_type or value—so conversion.conv_type and conversion.value[...] should be updated to the documented fields.
🧰 Tools
🪛 GitHub Actions: Python Lint (RationAI Standard) / 1_run _ MyPy.txt

[error] 3-3: mypy: Module "rationai.staining" has no attribute "ConversionType" [attr-defined]

🪛 GitHub Actions: Python Lint (RationAI Standard) / run _ MyPy

[error] 3-3: mypy: Module "rationai.staining" has no attribute "ConversionType" [attr-defined]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rationai/qc/staining/color_difference.py` at line 3, Replace the incorrect
import and field accesses to match the updated rationai-staining API: change the
import in this module to import ConversionDirection (not ConversionType)
alongside ColorConversion, and update all uses of conversion.conv_type to
conversion.direction and conversion.value[...] to use conversion.matrix or
conversion.inverse as appropriate; specifically inspect code paths that expect
RGB2STAIN/STAIN2RGB and switch logic to read conversion.direction, and pull the
transformation arrays from conversion.matrix (or the inverse matrix from
conversion.inverse) instead of conversion.value.

Source: Pipeline failures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant