Loading autocnet/spatial/overlap.py +191 −5 Original line number Diff line number Diff line Loading @@ -86,11 +86,8 @@ def place_points_in_overlap(overlap, Parameters ---------- nodes : list of Nodes The Nodes or Networknodes of all the images that intersect the overlap geom : geometry The geometry of the overlap region overlap : obj An autocnet.io.db.model Overlay model instance cam_type : str options: {"csm", "isis"} Loading Loading @@ -261,4 +258,193 @@ def place_points_in_overlap(overlap, points.append(point) print(f'Able to place {len(points)} points.') Points.bulkadd(points, ncg.Session) return points def place_points_in_image(image, cam_type="csm", size=71, distribute_points_kwargs={}, ncg=None, **kwargs): """ Place points into an image and then attempt to place measures into all overlapping images. This function is funcitonally identical to place_point_in_overlap except it works on individual images. Parameters ---------- image : obj An autocnet Images model object cam_type : str options: {"csm", "isis"} Pick what kind of camera model implementation to use size : int The size of the window used to extractor features to find an interesting feature to which the point is shifted. distirbute_points_kwargs : dict Of optional arguments for distirbute_points_in_geom """ # Arg checking if not ncg.Session: raise BrokenPipeError('This func requires a database session from a NetworkCandidateGraph.') # Determine what sensor type to use avail_cams = {"isis", "csm"} cam_type = cam_type.lower() if cam_type not in cam_type: raise Exception(f'{cam_type} is not one of valid camera: {avail_cams}') if not isinstance(image, Images): raise TypeError('image argument must be of type Images') # Logic geom = image.geom # Put down a grid of points over the image; the density is intentionally high valid = compgeom.distribute_points_in_geom(geom, **distribute_points_kwargs) points = [] print(f'Have {len(valid)} potential points to place.') for v in valid: lon = v[0] lat = v[1] point_geometry = f'SRID=949900;POINT({v[0]} {v[1]})' # Calculate the height, the distance (in meters) above or # below the aeroid (meters above or below the BCBF spheroid). px, py = ncg.dem.latlon_to_pixel(lat, lon) height = ncg.dem.read_array(1, [px, py, 1, 1])[0][0] with ncg.session_scope() as session: intersecting_images = session.query(Images.id, Images.path).filter(Images.geom.ST_Intersects(point_geometry)).all() node_res= [i for i in intersecting_images] nodes = [] for nid, image_path in node_res: # Setup the node objects that are covered by the geom nn = NetworkNode(node_id=nid, image_path=image_path) nn.parent = ncg nodes.append(nn) # Need to get the first node and then convert from lat/lon to image space node = nodes[0] if cam_type == "isis": try: line, sample = isis.ground_to_image(node["image_path"], lon, lat) except ProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(f'point ({lon}, {lat}) does not project to reference image {node["image_path"]}') continue if cam_type == "csm": lon_og, lat_og = oc2og(lon, lat, semi_major, semi_minor) x, y, z = reproject([lon_og, lat_og, height], semi_major, semi_minor, 'latlon', 'geocent') # The CSM conversion makes the LLA/ECEF conversion explicit gnd = csmapi.EcefCoord(x, y, z) image_coord = node.camera.groundToImage(gnd) sample, line = image_coord.samp, image_coord.line # Extract ORB features in a sub-image around the desired point image_roi = roi.Roi(node.geodata, sample, line, size_x=size, size_y=size) image = image_roi.clip() try: interesting = extract_most_interesting(image) except: continue # kps are in the image space with upper left origin and the roi # could be the requested size or smaller if near an image boundary. # So use the roi upper left_x and top_y for the actual origin. left_x, _, top_y, _ = image_roi.image_extent newsample = left_x + interesting.x newline = top_y + interesting.y # Get the updated lat/lon from the feature in the node if cam_type == "isis": try: p = isis.point_info(node["image_path"], newsample, newline, point_type="image") except ProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(node["image_path"]) print(f'interesting point ({newsample}, {newline}) does not project back to ground') continue try: x, y, z = p["BodyFixedCoordinate"].value except: x, y, z = ["BodyFixedCoordinate"] if getattr(p["BodyFixedCoordinate"], "units", "None").lower() == "km": x = x * 1000 y = y * 1000 z = z * 1000 elif cam_type == "csm": image_coord = csmapi.ImageCoord(newline, newsample) pcoord = node.camera.imageToGround(image_coord) # Get the BCEF coordinate from the lon, lat updated_lon_og, updated_lat_og, _ = reproject([pcoord.x, pcoord.y, pcoord.z], semi_major, semi_minor, 'geocent', 'latlon') updated_lon, updated_lat = og2oc(updated_lon_og, updated_lat_og, semi_major, semi_minor) px, py = ncg.dem.latlon_to_pixel(updated_lat, updated_lon) updated_height = ncg.dem.read_array(1, [px, py, 1, 1])[0][0] # Get the BCEF coordinate from the lon, lat x, y, z = reproject([updated_lon_og, updated_lat_og, updated_height], semi_major, semi_minor, 'latlon', 'geocent') # If the updated point is outside of the overlap, then revert back to the # original point and hope the matcher can handle it when sub-pixel registering updated_lon_og, updated_lat_og, updated_height = reproject([x, y, z], semi_major, semi_minor, 'geocent', 'latlon') updated_lon, updated_lat = og2oc(updated_lon_og, updated_lat_og, semi_major, semi_minor) if not geom.contains(shapely.geometry.Point(updated_lon, updated_lat)): lon_og, lat_og = oc2og(lon, lat, semi_major, semi_minor) x, y, z = reproject([lon_og, lat_og, height], semi_major, semi_minor, 'latlon', 'geocent') updated_lon_og, updated_lat_og, updated_height = reproject([x, y, z], semi_major, semi_minor, 'geocent', 'latlon') updated_lon, updated_lat = og2oc(updated_lon_og, updated_lat_og, semi_major, semi_minor) point_geom = shapely.geometry.Point(x, y, z) # Insert a spatial query to find which overlap this is in. with ncg.session_scope() as session: oid = session.query(Overlay.id).filter(Overlay.geom.ST_Contains(point_geometry)).one()[0] point = Points(overlapid=oid, apriori=point_geom, adjusted=point_geom, pointtype=2, # Would be 3 or 4 for ground cam_type=cam_type) for node in nodes: insert = True if cam_type == "csm": image_coord = node.camera.groundToImage(gnd) sample, line = image_coord.samp, image_coord.line if cam_type == "isis": try: line, sample = isis.ground_to_image(node["image_path"], updated_lon, updated_lat) except ProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(f'interesting point ({lon},{lat}) does not project to image {node["image_path"]}') insert = False point.measures.append(Measures(sample=sample, line=line, apriorisample=sample, aprioriline=line, imageid=node['node_id'], serial=node.isis_serial, measuretype=3, choosername='place_points_in_image')) if len(point.measures) >= 2: points.append(point) print(f'Able to place {len(points)} points.') Points.bulkadd(points, ncg.Session) Loading
autocnet/spatial/overlap.py +191 −5 Original line number Diff line number Diff line Loading @@ -86,11 +86,8 @@ def place_points_in_overlap(overlap, Parameters ---------- nodes : list of Nodes The Nodes or Networknodes of all the images that intersect the overlap geom : geometry The geometry of the overlap region overlap : obj An autocnet.io.db.model Overlay model instance cam_type : str options: {"csm", "isis"} Loading Loading @@ -261,4 +258,193 @@ def place_points_in_overlap(overlap, points.append(point) print(f'Able to place {len(points)} points.') Points.bulkadd(points, ncg.Session) return points def place_points_in_image(image, cam_type="csm", size=71, distribute_points_kwargs={}, ncg=None, **kwargs): """ Place points into an image and then attempt to place measures into all overlapping images. This function is funcitonally identical to place_point_in_overlap except it works on individual images. Parameters ---------- image : obj An autocnet Images model object cam_type : str options: {"csm", "isis"} Pick what kind of camera model implementation to use size : int The size of the window used to extractor features to find an interesting feature to which the point is shifted. distirbute_points_kwargs : dict Of optional arguments for distirbute_points_in_geom """ # Arg checking if not ncg.Session: raise BrokenPipeError('This func requires a database session from a NetworkCandidateGraph.') # Determine what sensor type to use avail_cams = {"isis", "csm"} cam_type = cam_type.lower() if cam_type not in cam_type: raise Exception(f'{cam_type} is not one of valid camera: {avail_cams}') if not isinstance(image, Images): raise TypeError('image argument must be of type Images') # Logic geom = image.geom # Put down a grid of points over the image; the density is intentionally high valid = compgeom.distribute_points_in_geom(geom, **distribute_points_kwargs) points = [] print(f'Have {len(valid)} potential points to place.') for v in valid: lon = v[0] lat = v[1] point_geometry = f'SRID=949900;POINT({v[0]} {v[1]})' # Calculate the height, the distance (in meters) above or # below the aeroid (meters above or below the BCBF spheroid). px, py = ncg.dem.latlon_to_pixel(lat, lon) height = ncg.dem.read_array(1, [px, py, 1, 1])[0][0] with ncg.session_scope() as session: intersecting_images = session.query(Images.id, Images.path).filter(Images.geom.ST_Intersects(point_geometry)).all() node_res= [i for i in intersecting_images] nodes = [] for nid, image_path in node_res: # Setup the node objects that are covered by the geom nn = NetworkNode(node_id=nid, image_path=image_path) nn.parent = ncg nodes.append(nn) # Need to get the first node and then convert from lat/lon to image space node = nodes[0] if cam_type == "isis": try: line, sample = isis.ground_to_image(node["image_path"], lon, lat) except ProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(f'point ({lon}, {lat}) does not project to reference image {node["image_path"]}') continue if cam_type == "csm": lon_og, lat_og = oc2og(lon, lat, semi_major, semi_minor) x, y, z = reproject([lon_og, lat_og, height], semi_major, semi_minor, 'latlon', 'geocent') # The CSM conversion makes the LLA/ECEF conversion explicit gnd = csmapi.EcefCoord(x, y, z) image_coord = node.camera.groundToImage(gnd) sample, line = image_coord.samp, image_coord.line # Extract ORB features in a sub-image around the desired point image_roi = roi.Roi(node.geodata, sample, line, size_x=size, size_y=size) image = image_roi.clip() try: interesting = extract_most_interesting(image) except: continue # kps are in the image space with upper left origin and the roi # could be the requested size or smaller if near an image boundary. # So use the roi upper left_x and top_y for the actual origin. left_x, _, top_y, _ = image_roi.image_extent newsample = left_x + interesting.x newline = top_y + interesting.y # Get the updated lat/lon from the feature in the node if cam_type == "isis": try: p = isis.point_info(node["image_path"], newsample, newline, point_type="image") except ProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(node["image_path"]) print(f'interesting point ({newsample}, {newline}) does not project back to ground') continue try: x, y, z = p["BodyFixedCoordinate"].value except: x, y, z = ["BodyFixedCoordinate"] if getattr(p["BodyFixedCoordinate"], "units", "None").lower() == "km": x = x * 1000 y = y * 1000 z = z * 1000 elif cam_type == "csm": image_coord = csmapi.ImageCoord(newline, newsample) pcoord = node.camera.imageToGround(image_coord) # Get the BCEF coordinate from the lon, lat updated_lon_og, updated_lat_og, _ = reproject([pcoord.x, pcoord.y, pcoord.z], semi_major, semi_minor, 'geocent', 'latlon') updated_lon, updated_lat = og2oc(updated_lon_og, updated_lat_og, semi_major, semi_minor) px, py = ncg.dem.latlon_to_pixel(updated_lat, updated_lon) updated_height = ncg.dem.read_array(1, [px, py, 1, 1])[0][0] # Get the BCEF coordinate from the lon, lat x, y, z = reproject([updated_lon_og, updated_lat_og, updated_height], semi_major, semi_minor, 'latlon', 'geocent') # If the updated point is outside of the overlap, then revert back to the # original point and hope the matcher can handle it when sub-pixel registering updated_lon_og, updated_lat_og, updated_height = reproject([x, y, z], semi_major, semi_minor, 'geocent', 'latlon') updated_lon, updated_lat = og2oc(updated_lon_og, updated_lat_og, semi_major, semi_minor) if not geom.contains(shapely.geometry.Point(updated_lon, updated_lat)): lon_og, lat_og = oc2og(lon, lat, semi_major, semi_minor) x, y, z = reproject([lon_og, lat_og, height], semi_major, semi_minor, 'latlon', 'geocent') updated_lon_og, updated_lat_og, updated_height = reproject([x, y, z], semi_major, semi_minor, 'geocent', 'latlon') updated_lon, updated_lat = og2oc(updated_lon_og, updated_lat_og, semi_major, semi_minor) point_geom = shapely.geometry.Point(x, y, z) # Insert a spatial query to find which overlap this is in. with ncg.session_scope() as session: oid = session.query(Overlay.id).filter(Overlay.geom.ST_Contains(point_geometry)).one()[0] point = Points(overlapid=oid, apriori=point_geom, adjusted=point_geom, pointtype=2, # Would be 3 or 4 for ground cam_type=cam_type) for node in nodes: insert = True if cam_type == "csm": image_coord = node.camera.groundToImage(gnd) sample, line = image_coord.samp, image_coord.line if cam_type == "isis": try: line, sample = isis.ground_to_image(node["image_path"], updated_lon, updated_lat) except ProcessError as e: if 'Requested position does not project in camera model' in e.stderr: print(f'interesting point ({lon},{lat}) does not project to image {node["image_path"]}') insert = False point.measures.append(Measures(sample=sample, line=line, apriorisample=sample, aprioriline=line, imageid=node['node_id'], serial=node.isis_serial, measuretype=3, choosername='place_points_in_image')) if len(point.measures) >= 2: points.append(point) print(f'Able to place {len(points)} points.') Points.bulkadd(points, ncg.Session)