diff --git a/autocnet/matcher/subpixel.py b/autocnet/matcher/subpixel.py index a74452fb2789a7aa1b2e7337272ff9a68ede0006..b1155c522c5809a97094dfb6e57db070b589e7fe 100644 --- a/autocnet/matcher/subpixel.py +++ b/autocnet/matcher/subpixel.py @@ -16,6 +16,7 @@ import cv2 from skimage import transform as tf from skimage import registration from skimage import filters +from skimage.util import img_as_float32 from scipy import fftpack from matplotlib import pyplot as plt @@ -523,7 +524,7 @@ def subpixel_template_classic(sx, sy, dx, dy, if (s_image is None) or (d_template is None): return None, None, None, None - shift_x, shift_y, metrics, corrmap = func(d_template.astype('float32'), s_image.astype('float32'), **kwargs) + shift_x, shift_y, metrics, corrmap = func(img_as_float32(d_template), img_as_float32(s_image), **kwargs) if shift_x is None: return None, None, None, None # Apply the shift and return @@ -1521,7 +1522,6 @@ def subpixel_register_point(pointid, for measure in measures: res = session.query(Images).filter(Images.id == measure.imageid).one() nodes[measure.imageid] = NetworkNode(node_id=measure.imageid, image_path=res.path) - session.expunge_all() resultlog = [] @@ -2172,41 +2172,46 @@ def subpixel_register_point_smart(pointid, t1 = time.time() with ncg.session_scope() as session: + # Order by is important here because the measures get ids in sequential order when initially placed + # and the reference_index is positionally linked to the ordered vector of measures. measures = session.query(Measures).filter(Measures.pointid == pointid).order_by(Measures.id).all() point = session.query(Points).filter(Points.id == pointid).one() reference_index = point.reference_index t2 = time.time() print(f'Query took {t2-t1} seconds to find the measures and reference measure.') - # Get the reference measure. Previously this was index 0, but now it is a database tracked attribute + + # Get the reference measure to instantiate the source node. All other measures will + # match to the source node. source = measures[reference_index] + reference_index_id = source.imageid print(f'Using measure {source.id} on image {source.imageid}/{source.serial} as the reference.') print(f'Measure reference index is: {reference_index}') - source.template_metric = 1 - source.template_shift = 0 - source.phase_error = 0 - source.phase_diff = 0 - source.phase_shift = 0 - - sourceid = source.imageid - sourceres = session.query(Images).filter(Images.id == sourceid).one() - source_node = NetworkNode(node_id=sourceid, image_path=sourceres.path) - source_node.parent = ncg - t3 = time.time() - print(f'Query for the image to use as source took {t3-t2} seconds.') - print(f'Attempting to subpixel register {len(measures)-1} measures for point {pointid}') + + # Build a node cache so that this is an encapsulated database call. Then nodes + # can be pulled from the lookup sans database. nodes = {} for measure in measures: res = session.query(Images).filter(Images.id == measure.imageid).one() - nodes[measure.imageid] = NetworkNode(node_id=measure.imageid, image_path=res.path) + nn = NetworkNode(node_id=measure.imageid, image_path=res.path) + nn.parent = ncg + nodes[measure.imageid] = nn session.expunge_all() + + t3 = time.time() + print(f'Query for the image to use as source took {t3-t2} seconds.') + print(f'Attempting to subpixel register {len(measures)-1} measures for point {pointid}') + print(nodes) + # Set the reference image + source_node = nodes[reference_index_id] print(f'Source: sample: {source.sample} | line: {source.line}') resultlog = [] updated_measures = [] for i, measure in enumerate(measures): + # If this is the reference node, do not attempt to match it. if i == reference_index: continue @@ -2270,8 +2275,12 @@ def subpixel_register_point_smart(pointid, updated_measures.append([None, None, m]) continue + base_roi = img_as_float32(base_roi) + dst_roi = img_as_float32(dst_roi) + baseline_mi = mutual_information(base_roi, dst_roi) + # Refactor this call to module result = cv2.matchTemplate(base_roi, dst_roi, method=cv2.TM_CCOEFF_NORMED) baseline_corr = result[0][0] @@ -2296,6 +2305,10 @@ def subpixel_register_point_smart(pointid, base_roi = roi.Roi(base_arr, source.apriorisample, source.aprioriline, size_x=size_x, size_y=size_y).clip() dst_roi = roi.Roi(dst_arr, x, y, size_x=size_x, size_y=size_y).clip() + #TODO: When refactored, all this type conversion should happen in the ROI object. + base_roi = img_as_float32(base_roi) + dst_roi = img_as_float32(dst_roi) + mi_metric = mutual_information(base_roi, dst_roi) if mi_metric is None: diff --git a/autocnet/spatial/overlap.py b/autocnet/spatial/overlap.py index 37af0dcb7a1280debaa676ac04ee46b983780875..965d5f3b0c0f406b9744740a21b095c20222d5b9 100644 --- a/autocnet/spatial/overlap.py +++ b/autocnet/spatial/overlap.py @@ -74,7 +74,7 @@ def place_points_in_overlaps(size_threshold=0.0007, if not ncg.Session: raise BrokenPipeError('This func requires a database session from a NetworkCandidateGraph.') - for overlap in Overlay.overlapping_larger_than(size_threshold, Session): + for overlap in Overlay.overlapping_larger_than(size_threshold, ncg.Session): if overlap.intersections == None: continue place_points_in_overlap(overlap, @@ -93,7 +93,7 @@ def place_points_in_overlap(overlap, use_cache=False, **kwargs): """ - Place points into an overlap geometry by back-projecing using sensor models. + Place points into an overlap geometry by back-projecting using sensor models. The DEM specified in the config file will be used to calculate point elevations. Parameters @@ -102,7 +102,7 @@ def place_points_in_overlap(overlap, An autocnet.io.db.model Overlay model instance. identifier: str - The tag used to distiguish points laid down by this function. + The tag used to distinguish points laid down by this function. cam_type : str options: {"csm", "isis"} @@ -292,18 +292,27 @@ def place_points_in_overlap(overlap, # Compute ground point to back project into measurtes gnd = csmapi.EcefCoord(x, y, z) - for node in nodes: + for current_index, node in enumerate(nodes): if cam_type == "csm": image_coord = node.camera.groundToImage(gnd) sample, line = image_coord.samp, image_coord.line if cam_type == "isis": + # If this try/except fails, then the reference_index could be wrong because the length + # of the measures list is different than the length of the nodes list that was used + # to find the most interesting feature. try: sample, line = isis.ground_to_image(node["image_path"], updated_lon, updated_lat) except CalledProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(f'interesting point ({updated_lon},{updated_lat}) does not project to image {node["image_path"]}') - continue - + # If the current_index is greater than the reference_index, the change in list size does + # not impact the positional index of the reference. If current_index is less than the + # reference_index, then the reference_index needs to de-increment by one for each time + # a measure fails to be placed. + if current_index < reference_index: + reference_index -= 1 + continue + point.measures.append(Measures(sample=sample, line=line, apriorisample=sample,