Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions httomolib/misc/blend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------------
# Copyright 2023 Diamond Light Source Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ---------------------------------------------------------------------------
# Created By : Tomography Team at DLS <scientificsoftware@diamond.ac.uk>
# Created Date: 30 April 2026
# ---------------------------------------------------------------------------
"""Module for data blending functions"""

import numpy as np
from typing import Optional

from httomolib.misc.utils import (
__check_variable_type,
__check_if_data_3D_array,
__check_if_data_correct_type,
)

__all__ = [
"seam_blend_stitched_data",
]


def seam_blend_stitched_data(
data: np.ndarray,
seam_index: Optional[int] = None,
blending_width: Optional[int] = None,
path_to_stiched_params_file: Optional[str] = None,
shift_seam_index: int = 0,
) -> np.ndarray:
"""
Function blends the seam present in the stitched projection data. It uses the redundant by blending_width data present on both sides of the seam.
Used in HTTomo for seamless stitching of datasets coming from two different PCO cameras.

Parameters
----------
data : np.ndarray
3d array of the stitched data, assuming the following axis ["angles", "detY", "detX"].
seam_index : Optional, int
The horizontal index of the seam along the 'detX' axis. If None and 'path_to_stiched_params_file' is provided, it will be taken from the file, otherwise middle of the horizontal axis.
blending_width : Optional, int
The area for symmetric blending (e.g. with the ramp filter) around the seam position (seam_index) of the stitched data. If None and 'path_to_stiched_params_file' is provided, it will be taken from the file, otherwise 0.
path_to_stiched_params_file : Optional, str
Path to the text file with the stiching parameters. If provided 'seam_index' and 'blending_width' parameters will be overridden by the ones provided in the file.
shift_seam_index : int
performs a shift of the seam index with seam_index - shift_seam_index. This is purely an HTTomo related feature and should be ignored by users.
Raises
----------
ValueError: When data is not 3D.

Returns
----------
np.ndarray: stitched data without the seam.
"""
### Data and parameters checks ###
methods_name = "seam_blender"
__check_if_data_3D_array(data, methods_name)
__check_if_data_correct_type(
data,
accepted_type=["float64", "float32", "uint8", "uint16", "uint32"],
methods_name=methods_name,
)
__check_variable_type(seam_index, [int, type(None)], "seam_index", [], methods_name)
__check_variable_type(
blending_width, [int, type(None)], "blending_width", [], methods_name
)
__check_variable_type(
path_to_stiched_params_file,
[str, type(None)],
"path_to_stiched_params_file",
[],
methods_name,
)
###################################

angles_dim, detY, detX = data.shape

if path_to_stiched_params_file is not None:
params = {}
with open(path_to_stiched_params_file) as f:
for line in f:
key, value = line.split()
params[key] = int(value)

blending_width = params.get("blending_width")
seam_index = params.get("seam_index")

if blending_width is None:
blending_width = 0
if seam_index is None:
seam_index = int(detX // 2)

blending_width *= 2
seam_index -= shift_seam_index

if seam_index >= detX - blending_width:
err_str = f"Seam index given as '{seam_index}' must be smaller than the horizontal dimension of the data '{detX}' minus blending width."
raise ValueError(err_str)

# Split regions of data
left_part = data[:, :, 0 : (seam_index - blending_width)]
right_part = data[:, :, (seam_index + blending_width) : :]

# Overlap regions
left_overlap = data[:, :, (seam_index - blending_width) : seam_index]
right_overlap = data[:, :, seam_index : (seam_index + blending_width)]

# Create ramp weights (0 → 1)
ramp = np.float32(np.linspace(0, 1, blending_width))
ramp = np.tile(ramp, (detY, 1))

# Blend
blended_overlap = (1 - ramp) * left_overlap + ramp * right_overlap

return np.dstack([left_part, blended_overlap, right_part])
12 changes: 12 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ def data_file(test_data_path):
return np.load(in_file)


@pytest.fixture(scope="session")
def data_stitched(test_data_path):
in_file = os.path.join(test_data_path, "stitched_blend30_seam151.npy")
return np.load(in_file)


@pytest.fixture(scope="session")
def data_stitched_txt(test_data_path):
in_file = os.path.join(test_data_path, "stitching_params.txt")
return in_file


@pytest.fixture
def host_data(data_file):
return np.copy(data_file["data"])
Expand Down
Binary file added tests/test_data/stitched_blend30_seam151.npy
Binary file not shown.
2 changes: 2 additions & 0 deletions tests/test_data/stitching_params.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
blending_width 30
seam_index 151
23 changes: 23 additions & 0 deletions tests/test_misc/test_blend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import numpy as np

from httomolib.misc.blend import seam_blend_stitched_data


def test_seam_blend_stitched_data(data_stitched):

result = seam_blend_stitched_data(data_stitched, seam_index=151, blending_width=30)

assert result.flags.c_contiguous
assert result.dtype == np.float32
assert result.shape == (300, 4, 240)


def test_seam_blend_stitched_data_txt(data_stitched, data_stitched_txt):

result = seam_blend_stitched_data(
data_stitched, path_to_stiched_params_file=data_stitched_txt
)

assert result.flags.c_contiguous
assert result.dtype == np.float32
assert result.shape == (300, 4, 240)
Loading