Skip to content

Commit

Permalink
add the ability to insert a tile post-stitching
Browse files Browse the repository at this point in the history
this is ... mostly successful. the patch-up tiles have to have
"roughly" the same 0,0 coordinate for anything to work, but it
seems to have worked on at least a couple of cases
  • Loading branch information
bunnie committed Feb 5, 2024
1 parent 9483001 commit bda971b
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 21 deletions.
13 changes: 10 additions & 3 deletions schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,13 @@ def add_tile(self, fpath, max_x = None, max_y = None):
self.schema['tiles'][str(self.auto_index)] = tile
self.log_to_undo('add', self.auto_index, {})
self.auto_index += 1
self.coords_mm += [(metadata['x'], metadata['y'])]

if type(self.coords_mm) == np.ndarray:
# handle case that the coords_mm variable has been transformed into a numpy array, which
# overloads `+` to mean "add these numbers to every offset of the array" versus "append"
self.coords_mm = np.concatenate((self.coords_mm, [[metadata['x'], metadata['y']]]))
else:
self.coords_mm += [(metadata['x'], metadata['y'])]

def contains_layer(self, layer):
return self.schema['tiles'].get(str(layer)) is not None
Expand Down Expand Up @@ -238,6 +244,8 @@ def finalize(self):
if x_res * THUMB_SCALE < THUMB_THRESHOLD_PX \
or y_res * THUMB_SCALE < THUMB_THRESHOLD_PX:
THUMB_SCALE = 1.0
# ensure auto-add index doesn't overwrite an existing tile.
self.auto_index = int(max(self.schema['tiles'].keys())) + 1

def closest_tile_to_coord_mm(self, coord_um):
offset_coords_mm = []
Expand Down Expand Up @@ -274,8 +282,7 @@ def remove_tile(self, layer):
assert len(new_coords_mm) == len(self.coords_mm) - 1, "Did not remove exactly one element from coords_mm"
self.coords_mm = new_coords_mm
# regenerate convenience lists
self.x_list = np.unique(np.rot90(self.coords_mm)[1])
self.y_list = np.unique(np.rot90(self.coords_mm)[0])
self.finalize()

def get_tile_by_coordinate(self, coord):
for (layer, t) in self.schema['tiles'].items():
Expand Down
26 changes: 23 additions & 3 deletions stitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

import argparse
from pathlib import Path
from shutil import copyfile
import logging
import sys
import threading

from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import (QLabel, QApplication, QWidget, QDesktopWidget,
QCheckBox, QMessageBox, QMainWindow, QPushButton,
from PyQt5.QtWidgets import (QLabel, QApplication, QWidget, QFileDialog,
QCheckBox, QMainWindow, QPushButton,
QComboBox, QSlider, QGroupBox, QGridLayout, QBoxLayout,
QHBoxLayout, QVBoxLayout, QMenu, QAction, QFrame,
QHBoxLayout, QVBoxLayout,
QSizePolicy, QFormLayout, QLineEdit, QSpinBox)

from schema import Schema
Expand Down Expand Up @@ -171,6 +172,8 @@ def __init__(self):
self.status_mse_visualize_button.clicked.connect(self.on_mse_visualize)
self.status_remove_tile_button = QPushButton("Remove Selected")
self.status_remove_tile_button.clicked.connect(self.on_remove_selected)
self.status_patch_tile_button = QPushButton("Add Tile")
self.status_patch_tile_button.clicked.connect(self.on_patch_tile)
self.status_undo_button = QPushButton("Undo")
self.status_undo_button.clicked.connect(self.on_undo_button)
status_overall_layout.addWidget(self.status_redraw_button)
Expand All @@ -184,6 +187,7 @@ def __init__(self):
status_overall_layout.addWidget(self.status_restitch_selection_button)
status_overall_layout.addWidget(self.status_flag_manual_review_button)
status_overall_layout.addWidget(self.status_remove_tile_button)
status_overall_layout.addWidget(self.status_patch_tile_button)
status_overall_layout.addWidget(self.status_undo_button)
status_overall_layout.addStretch()
status_overall_layout.addWidget(self.status_save_button)
Expand Down Expand Up @@ -281,11 +285,26 @@ def on_flag_manual_review(self):
def on_remove_selected(self):
self.schema.set_undo_checkpoint()
(layer, _tile) = self.schema.get_tile_by_coordinate(self.selected_image_centroid)
self.selected_image_centroid = None
self.schema.remove_tile(layer)
self.redraw_overview()
if self.zoom_window_opened:
self.update_zoom_window()

def on_patch_tile(self):
file_name = QFileDialog.getOpenFileName(self,
"Open Image", str(self.schema.path), "Image Files (*.png *.jpg *.bmp)")[0]
p = Path(file_name)
self.schema.add_tile(p)
if p.parent != self.schema.path.resolve():
copyfile(p, self.schema.path / p.name)
thumbnail_kernel(self.args, self.schema.path / p.name)
self.schema.finalize()
self.schema.set_undo_checkpoint()
self.redraw_overview()
if self.zoom_window_opened:
self.update_zoom_window()

def on_redraw_button(self):
self.redraw_overview()
if self.zoom_window_opened:
Expand Down Expand Up @@ -479,6 +498,7 @@ def main():

app = QApplication(sys.argv)
w = MainWindow()
w.args = args
w.setGeometry(200, 200, 2000, 2400)

w.schema = Schema(use_cache=not args.no_caching)
Expand Down
55 changes: 40 additions & 15 deletions template_stitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,16 @@ def stitch_auto_template_linear(self, stitch_list=None, mse_cleanup=False):
logging.info("Auto-stitch pass done")
return restart

def check_and_add_layer(layer, t, ref_layers):
if layer is not None:
if t['auto_error'] != 'false':
logging.warning(f"Skipping candidate layer {layer} because it has a bad stitch")
else:
ref_layers += [layer]
return True
else:
return False

def restitch_one(self, moving_layer, mse_cleanup=False):
self.schema.set_undo_checkpoint()
# search for nearby tiles that have large overlaps and make them reference layers
Expand All @@ -1308,27 +1318,42 @@ def restitch_one(self, moving_layer, mse_cleanup=False):
# katty corner back
if next_lower_y_mm is not None and next_lower_x_mm is not None:
(layer, t) = self.schema.get_tile_by_coordinate((next_lower_x_mm, next_lower_y_mm))
if layer is not None:
if t['auto_error'] != 'false':
logging.warning(f"Skipping candidate layer {layer} because it has a bad stitch")
else:
ref_layers += [layer]
if not check_and_add_layer(layer, t, ref_layers):
# this is likely a "patch" image that was taken in a different run; add a bunch of guesses nearby
if y_i > 1:
(layer, t) = self.schema.get_tile_by_coordinate((next_lower_x_mm, sorted_y[y_i - 2]))
check_and_add_layer(layer, t, ref_layers)
if y_i > 1 and x_i + 1 < len(sorted_x):
(layer, t) = self.schema.get_tile_by_coordinate((sorted_x[x_i + 1], sorted_y[y_i - 2]))
check_and_add_layer(layer, t, ref_layers)
if y_i + 2 < len(sorted_y):
(layer, t) = self.schema.get_tile_by_coordinate((next_lower_x_mm, sorted_y[y_i + 2]))
check_and_add_layer(layer, t, ref_layers)
if y_i + 2 < len(sorted_y) and x_i + 1 < len(sorted_x):
(layer, t) = self.schema.get_tile_by_coordinate((sorted_x[x_i + 1], sorted_y[y_i + 2]))
check_and_add_layer(layer, t, ref_layers)
# up
if next_lower_y_mm is not None:
(layer, t) = self.schema.get_tile_by_coordinate((moving_x_mm, next_lower_y_mm))
if layer is not None:
if t['auto_error'] != 'false':
logging.warning(f"Skipping candidate layer {layer} because it has a bad stitch")
else:
ref_layers += [layer]
if not check_and_add_layer(layer, t, ref_layers):
# this is likely a "patch" image that was taken in a different run; add a bunch of guesses nearby
if x_i + 1 < len(sorted_x):
(layer, t) = self.schema.get_tile_by_coordinate((sorted_x[x_i + 1], next_lower_y_mm))
check_and_add_layer(layer, t, ref_layers)
if x_i - 1 > 0:
(layer, t) = self.schema.get_tile_by_coordinate((sorted_x[x_i - 1], next_lower_y_mm))
check_and_add_layer(layer, t, ref_layers)
# left
if next_lower_x_mm is not None:
(layer, t) = self.schema.get_tile_by_coordinate((next_lower_x_mm, moving_y_mm))
if layer is not None:
if t['auto_error'] != 'false':
logging.warning(f"Skipping candidate layer {layer} because it has a bad stitch")
else:
ref_layers += [layer]
if not check_and_add_layer(layer, t, ref_layers):
# this is likely a "patch" image that was taken in a different run; add a bunch of guesses nearby
if y_i + 1 < len(sorted_y):
(layer, t) = self.schema.get_tile_by_coordinate((next_lower_x_mm, sorted_y[y_i + 1]))
check_and_add_layer(layer, t, ref_layers)
if y_i - 1 < 0:
(layer, t) = self.schema.get_tile_by_coordinate((next_lower_x_mm, sorted_y[y_i - 1]))
check_and_add_layer(layer, t, ref_layers)

self.stitch_one_template(
self.schema,
Expand Down

0 comments on commit bda971b

Please sign in to comment.