Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 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
c8b83e5
style: change flip_image to pass attribute to make change
stevenhua0320 Nov 3, 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
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)
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:
Copy link
Contributor

Choose a reason for hiding this comment

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

this logic needs to be changed in the new structure. Though I see that there is a bunch of logic in here that makes this more complicated than I thought. We should decide if we actually want to do this as it is a larger refactoring than we originally took on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should take this as a larger refactor because therer are a lot of usages here as I refactor for load_image function to correctly read the directory. And since here in the function flip is defined as a binary variable, most probably here Xiaohao wanted to filter out whether there is fliphorizontal or flipvertical action. If one of them is true then this flip binary variable is true. The problem is that in the load_image function it has integrated the flip_image functionality. So I'm unsure about whether we need to do it again here. For now, I think we should focus on the original process of release and probably create an issue for this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I know what happened here. There were in the old days a program called fit2D and now a program called pyfai and they used different definitions for the origin (the zero) of the image from top left to bottom left or sthg like that, so there was a particular flipping that we would often find. In general, we would like the code to be more general, so a more complete refactor could be done, but I don't think it is worth it as not many people are using this atm. So if we can keep what you have done, because it is good and we have more tests, but make sure that the code kind of behaves the same way as before, so flip can be either True or False and when it is True it is the flip that Xiahao was doing, then if you can assure me that this is the case, I will just merge this.

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,
}
59 changes: 59 additions & 0 deletions tests/test_load_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import os
import shutil
from pathlib import Path

import numpy as np
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 = ?
expected_last_point = ?

# 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:
cfg = type(
"Cfg", (), {"fliphorizontal": False, "flipvertical": False}
)()
loader = LoadImage(cfg)
actual = loader.load_image(file_name)
assert actual.shape == expected_shape
assert actual.mean() == expected_mean
assert actual[0] == expected_first_point
assert actual[-1] == expected_last_point
except FileNotFoundError:
pytest.raises(
FileNotFoundError,
match=r"file not found:"
r" .*Please rerun specifying a valid filename\.",
)
Loading