Skip to content

Commit

Permalink
Merge pull request #183 from yfukai/filtering_algorithm_update
Browse files Browse the repository at this point in the history
Filtering algorithm update
  • Loading branch information
yfukai authored Feb 13, 2022
2 parents f81d4c9 + 444c517 commit 4d391c9
Show file tree
Hide file tree
Showing 14 changed files with 430 additions and 363 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"python.testing.pytestArgs": ["tests"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
2 changes: 1 addition & 1 deletion LICENSE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ The original license for MIST (https://github.com/usnistgov/MIST/blob/27a8787/LI

NIST-developed software is provided by NIST as a public service. You may use, copy and distribute copies of the software in any medium, provided that you keep intact this entire notice. You may improve, modify and create derivative works of the software or any portion of the software, and you may copy and distribute such modifications or works. Modified works should carry a notice stating that you changed the software and should note the date and nature of any such change. Please explicitly acknowledge the National Institute of Standards and Technology as the source of the software.

NIST-developed software is expressly provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT OR ARISING BY OPERATION OF LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NIST NEITHER REPRESENTS NOR WARRANTS THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT ANY DEFECTS WILL BE CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR THE RESULTS THEREOF, INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY, RELIABILITY, OR USEFULNESS OF THE SOFTWARE.
NIST-developed software is expressly provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT OR ARISING BY OPERATION OF LAsizeY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NIST NEITHER REPRESENTS NOR WARRANTS THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT ANY DEFECTS WILL BE CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR THE RESULTS THEREOF, INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY, RELIABILITY, OR USEFULNESS OF THE SOFTWARE.

You are solely responsible for determining the appropriateness of using and distributing the software and you assume all risks associated with its use, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and the unavailability or interruption of operation. This software is not intended to be used in any situation where a failure could cause risk of injury or damage to property. The software developed by NIST employees is not subject to copyright protection within the United States.
4 changes: 2 additions & 2 deletions examples/stitching_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
props_file_path = path.join(script_path, "../tests/data/testimages_props.csv")
images = np.load(image_file_path)
props = pd.read_csv(props_file_path, index_col=0)
rows = props["row"].to_list()
cols = props["col"].to_list()
rows = props["col"].to_list()
cols = props["row"].to_list()

print(images.shape)
# must be 3-dim, with each dimension meaning (tile_index,x,y)
Expand Down
5 changes: 4 additions & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ def safety(session: Session) -> None:
"""Scan dependencies for insecure packages."""
requirements = session.poetry.export_requirements()
session.install("safety")
session.run("safety", "check", "--full-report", f"--file={requirements}")
session.run(
"safety", "check", "--full-report", f"--file={requirements}", "--ignore=44715"
)
# ignore numpy update for a while as resolved in 1.22 (2022.2.13)


@session(python=python_versions)
Expand Down
154 changes: 116 additions & 38 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "m2stitch"
version = "0.3.0"
version = "0.4.0"
description = "M2Stitch"
authors = ["Yohsuke Fukai <[email protected]>"]
license = "MIT"
Expand Down Expand Up @@ -32,6 +32,7 @@ pandas-stubs = "^1.2.0"
networkx = "^2.5.1"
scipy = "^1.6.3"
tqdm = "^4.60.0"
scikit-learn = "^1.0.2"

[tool.poetry.dev-dependencies]
pytest = "^7.0.0"
Expand Down
4 changes: 2 additions & 2 deletions src/m2stitch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ def main() -> None:

# testimages, props = test_image_path
# """It exits with a status code of zero."""
# rows = props["row"].to_list()
# cols = props["col"].to_list()
# rows = props["col"].to_list()
# cols = props["row"].to_list()
# result_df, _ = stitch_images(testimages, rows, cols)


Expand Down
30 changes: 18 additions & 12 deletions src/m2stitch/_constrained_refinement.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,23 @@ def refine_translations(images: NumArray, grid: pd.DataFrame, r: Float) -> pd.Da
images : NumArray
the tile images
grid : pd.DataFrame
the dataframe for the grid position, with columns "{top|left}_{x|y}_second"
the dataframe for the grid position, with columns "{left|top}_{x|y}_second"
r : Float
the repeatability
Returns
-------
grid : pd.DataFrame
the refined grid position, with columns "{top|left}_{x|y|ncc}"
the refined grid position, with columns "{left|top}_{x|y|ncc}"
"""
for direction in ["top", "left"]:
for direction in ["left", "top"]:
for i2, g in tqdm(grid.iterrows(), total=len(grid)):
i1 = g[direction]
if pd.isna(i1):
continue
image1 = images[i1]
image2 = images[i2]
W, H = image1.shape
sizeY, sizeX = image1.shape

def overlap_ncc(params):
x, y = params
Expand All @@ -99,21 +99,27 @@ def overlap_ncc(params):
return ncc(subI1, subI2)

init_values = [
int(g[f"{direction}_x_second"]),
int(g[f"{direction}_y_second"]),
int(g[f"{direction}_x_second"]),
]
limits = [
[max(-W + 1, init_values[0] - r), min(W - 1, init_values[0] + r)],
[max(-H + 1, init_values[1] - r), min(H - 1, init_values[1] + r)],
[
max(-sizeY + 1, init_values[0] - r),
min(sizeY - 1, init_values[0] + r),
],
[
max(-sizeX + 1, init_values[1] - r),
min(sizeX - 1, init_values[1] + r),
],
]
values, ncc_value = find_local_max_integer_constrained(
overlap_ncc, np.array(init_values), np.array(limits)
)
grid.loc[i2, f"{direction}_x"] = values[0]
grid.loc[i2, f"{direction}_y"] = values[1]
grid.loc[i2, f"{direction}_y"] = values[0]
grid.loc[i2, f"{direction}_x"] = values[1]
grid.loc[i2, f"{direction}_ncc"] = ncc_value
for direction in ["top", "left"]:
for xy in ["x", "y"]:
key = f"{direction}_{xy}"
for direction in ["left", "top"]:
for dim in "yx":
key = f"{direction}_{dim}"
grid[key] = grid[key].astype(pd.Int32Dtype())
return grid
25 changes: 12 additions & 13 deletions src/m2stitch/_global_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def compute_maximum_spanning_tree(grid: pd.DataFrame) -> nx.Graph:
----------
grid : pd.DataFrame
the dataframe for the grid position,
with columns "{top|left}_{x|y|ncc|valid3}"
with columns "{left|top}_{x|y|ncc|valid3}"
Returns
-------
Expand All @@ -21,7 +21,7 @@ def compute_maximum_spanning_tree(grid: pd.DataFrame) -> nx.Graph:
"""
connection_graph = nx.Graph()
for i, g in grid.iterrows():
for direction in ["top", "left"]:
for direction in ["left", "top"]:
if not pd.isna(g[direction]):
weight = g[f"{direction}_ncc"]
if g[f"{direction}_valid3"]:
Expand All @@ -33,8 +33,8 @@ def compute_maximum_spanning_tree(grid: pd.DataFrame) -> nx.Graph:
direction=direction,
f=i,
t=g[direction],
x=g[f"{direction}_x"],
y=g[f"{direction}_y"],
x=g[f"{direction}_x"],
)
return nx.maximum_spanning_tree(connection_graph)

Expand All @@ -58,8 +58,8 @@ def compute_final_position(
grid : pd.DataFrame
the result dataframe for the grid position, with columns "{x|y}_pos"
"""
grid.loc[source_index, "x_pos"] = 0
grid.loc[source_index, "y_pos"] = 0
grid.loc[source_index, "x_pos"] = 0

nodes = [source_index]
walked_nodes = []
Expand All @@ -72,20 +72,19 @@ def compute_final_position(
props["t"] == node
) & (props["f"] == adj)
nodes.append(adj)
x_pos = grid.loc[node, "x_pos"]
y_pos = grid.loc[node, "y_pos"]
x_pos = grid.loc[node, "x_pos"]

if node == props["t"]:
grid.loc[adj, "x_pos"] = x_pos + props["x"]
grid.loc[adj, "y_pos"] = y_pos + props["y"]
grid.loc[adj, "x_pos"] = x_pos + props["x"]
else:
grid.loc[adj, "x_pos"] = x_pos - props["x"]
grid.loc[adj, "y_pos"] = y_pos - props["y"]
assert not any(pd.isna(grid["x_pos"]))
assert not any(pd.isna(grid["y_pos"]))
grid["x_pos"] = grid["x_pos"] - grid["x_pos"].min()
grid["y_pos"] = grid["y_pos"] - grid["y_pos"].min()
grid["x_pos"] = grid["x_pos"].astype(np.int32)
grid["y_pos"] = grid["y_pos"].astype(np.int32)
grid.loc[adj, "x_pos"] = x_pos - props["x"]
for dim in "yx":
k = f"{dim}_pos"
assert not any(pd.isna(grid[k]))
grid[k] = grid[k] - grid[k].min()
grid[k] = grid[k].astype(np.int32)

return grid
Loading

0 comments on commit 4d391c9

Please sign in to comment.