Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c124f16
fix: fix loadimage function to robustly load image.
stevenhua0320 Oct 25, 2025
757b223
chore: add news item
stevenhua0320 Oct 25, 2025
3f4f892
chore: change docstring.
stevenhua0320 Oct 25, 2025
9a28a4b
tests: add test to loadimage
stevenhua0320 Oct 25, 2025
b2cc5c5
test: change test format.
stevenhua0320 Oct 26, 2025
0383f1f
test: change test to only catch function behavior.
stevenhua0320 Oct 26, 2025
6124e29
fix: Fix directory display so that CI can identify
stevenhua0320 Oct 26, 2025
709b48c
tests: change file-not-found to raise FileNotFound error.
stevenhua0320 Oct 27, 2025
e34638e
tests: change copy of example to temp folder & not using logic in test
stevenhua0320 Oct 27, 2025
22a2bcd
chore: change test name
stevenhua0320 Oct 27, 2025
e5685ca
chore: delete example.tiff
stevenhua0320 Oct 28, 2025
8b00059
tests: Change case 4 to FileNotFoundError.
stevenhua0320 Oct 29, 2025
98c7220
Fix: Fix news message and rename to comply PEP-8 standard.
stevenhua0320 Oct 31, 2025
46f175b
fix: change test logic to make it constant.
stevenhua0320 Oct 31, 2025
45f2952
style: change parametrization style.
stevenhua0320 Oct 31, 2025
4369cc2
Refactor image loading test with new expected values
sbillinge Oct 31, 2025
556e141
test: add starting point and last point for test.
stevenhua0320 Oct 31, 2025
ac7ece7
test: add test for flip_image mechanism.
stevenhua0320 Nov 1, 2025
1567b45
test: add more points in validating flip.
stevenhua0320 Nov 1, 2025
03ae177
style: change flip_image parameter to make non-flipping default.
stevenhua0320 Nov 1, 2025
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
File renamed without changes.
23 changes: 23 additions & 0 deletions news/load-image-fix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Added:**

* <news item>

**Changed:**

* <news item>

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* `load_image()` function correctly finds files when passed a relative path.

**Security:**

* <news item>
63 changes: 38 additions & 25 deletions src/diffpy/srxplanar/loadimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import fnmatch
import os
import time
from pathlib import Path

import numpy as np

Expand All @@ -24,14 +25,14 @@
try:
import fabio

def openImage(im):
def open_image(im):
rv = fabio.openimage.openimage(im)
return rv.data

except ImportError:
import tifffile

def openImage(im):
def open_image(im):
rv = tifffile.imread(im)
return rv

Expand All @@ -53,7 +54,7 @@ def __init__(self, p):
self.config = p
return

def flipImage(self, pic):
def flip_image(self, pic):
"""Flip image if configured in config.

:param pic: 2d array, image array
Expand All @@ -65,32 +66,44 @@ def flipImage(self, pic):
pic = np.array(pic[::-1, :])
return pic

def loadImage(self, filename):
"""Load image file, if failed (for example loading an incomplete
file), then it will keep trying loading file for 5s.
def load_image(self, filename):
"""Load image file. If loading fails (e.g. incomplete file),
retry for 5 seconds (10×0.5s).

:param filename: str, image file name
:return: 2d ndarray, 2d image array (flipped)
:param filename: str or Path, image file name or path
:return: 2D ndarray, flipped image array
"""
if os.path.exists(filename):
filename = Path(
filename
).expanduser() # handle "~", make it a Path object
if filename.exists():
filenamefull = filename
else:
filenamefull = os.path.join(self.opendirectory, filename)
image = np.zeros(10000).reshape(100, 100)
if os.path.exists(filenamefull):
i = 0
while i < 10:
try:
if os.path.splitext(filenamefull)[-1] == ".npy":
image = np.load(filenamefull)
else:
image = openImage(filenamefull)
i = 10
except FileNotFoundError:
i = i + 1
time.sleep(0.5)
image = self.flipImage(image)
image[image < 0] = 0
found_files = list(Path.home().rglob(filename.name))
filenamefull = found_files[0] if found_files else None

if filenamefull is None or not filenamefull.exists():
raise FileNotFoundError(
f"Error: file not found: {filename}, "
f"Please rerun specifying a valid filename."
)
return np.zeros((100, 100))

image = np.zeros((100, 100))
for _ in range(10): # retry 10 times (5 seconds total)
try:
if filenamefull.suffix == ".npy":
image = np.load(filenamefull)
else:
image = open_image(
str(filenamefull)
Copy link
Contributor

Choose a reason for hiding this comment

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

Per the conversation above, didn't we want to remove this str? That was my point actually

) # openImage expects str
break
except FileNotFoundError:
time.sleep(0.5)

image = self.flip_image(image)
Copy link
Contributor

Choose a reason for hiding this comment

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

Per conversation above, move this or of the function, or allow flipping as optional inputs.

image[image < 0] = 0
return image

def genFileList(
Expand Down
8 changes: 4 additions & 4 deletions src/diffpy/srxplanar/srxplanar.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def _getPic(self, image, flip=None, correction=None):
rv += self._getPic(imagefile)
rv /= len(image)
elif isinstance(image, str):
rv = self.loadimage.loadImage(image)
rv = self.loadimage.load_image(image)
if correction is None or correction is True:
ce = self.config.cropedges
rv[ce[2] : -ce[3], ce[0] : -ce[1]] = (
Expand All @@ -165,7 +165,7 @@ def _getPic(self, image, flip=None, correction=None):
else:
rv = image
if flip is True:
rv = self.loadimage.flipImage(rv)
rv = self.loadimage.flip_image(rv)
if correction is True:
# rv *= self.correction
ce = self.config.cropedges
Expand Down Expand Up @@ -339,13 +339,13 @@ def createMask(self, filename=None, pic=None, addmask=None):
pic = self.pic
else:
pic = (
self.loadimage.loadImage(filelist[0])
self.loadimage.load_image(filelist[0])
if len(filelist) > 0
else None
)
else:
pic = (
self.loadimage.loadImage(filelist[0])
self.loadimage.load_image(filelist[0])
if len(filelist) > 0
else None
)
Expand Down
19 changes: 14 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@
@pytest.fixture
def user_filesystem(tmp_path):
base_dir = Path(tmp_path)
home_dir = base_dir / "home_dir"
home_dir.mkdir(parents=True, exist_ok=True)
cwd_dir = base_dir / "cwd_dir"
cwd_dir.mkdir(parents=True, exist_ok=True)
home_dir = base_dir / "home_dir"
test_dir = base_dir / "test_dir"
for dir in (cwd_dir, home_dir, test_dir):
dir.mkdir(parents=True, exist_ok=True)

home_config_data = {"username": "home_username", "email": "home@email.com"}
home_config_data = {
"username": "home_username",
"email": "home@email.com",
}
with open(home_dir / "diffpyconfig.json", "w") as f:
json.dump(home_config_data, f)

yield tmp_path
yield {
"base": base_dir,
"cwd": cwd_dir,
"home": home_dir,
"test": test_dir,
}
58 changes: 58 additions & 0 deletions tests/test_load_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import os
import shutil
from pathlib import Path
from unittest.mock import Mock
Copy link
Contributor

Choose a reason for hiding this comment

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

we don't ever use unittest.mock, we use pytest.mock, but as discussed below, I don't think we should be mocking anything here.


import pytest

from diffpy.srxplanar.loadimage import LoadImage

PROJECT_ROOT = Path(__file__).resolve().parents[1]

load_image_param = [
# case 1: just filename of file in current directory.
# expect function loads tiff file from cwd
"KFe2As2-00838.tif",
# case 2: absolute file path to file in another directory.
# expect file is found and correctly read.
"home_dir/KFe2As2-00838.tif",
# case 3: relative file path to file in another directory.
# expect file is found and correctly read
"./KFe2As2-00838.tif",
# case 4: non-existent file that incurred by mistype.
"nonexistent_file.tif",
]


@pytest.mark.parametrize("file_name", load_image_param)
def test_load_image(file_name, user_filesystem):
home_dir = user_filesystem["home"]
cwd_dir = user_filesystem["cwd"]
os.chdir(cwd_dir)

# These values were obtained from docs/examples/data/KFe2As2-00838.tif
expected_mean = 2595.7087
expected_shape = (2048, 2048)
expected_first_point = 0
expected_last_point = 0

# locate source example file inside project docs
source_file = (
PROJECT_ROOT / "docs" / "examples" / "data" / "KFe2As2-00838.tif"
)
shutil.copy(source_file, cwd_dir / "KFe2As2-00838.tif")
shutil.copy(source_file, home_dir / "KFe2As2-00838.tif")

try:
loader = LoadImage(Mock(fliphorizontal=False, flipvertical=False))
actual = loader.load_image(file_name)
assert actual.shape == expected_shape
assert actual.mean() == expected_mean
assert actual[0][0] == expected_first_point
assert actual[-1][-1] == expected_last_point
except FileNotFoundError:
pytest.raises(
FileNotFoundError,
match=r"file not found:"
r" .*Please rerun specifying a valid filename\.",
)
Loading