def test_compute_residuals(control_network, sensors): # sensor.groundToImage.side_effect = [csmapi.ImageCoord(-0.1, 7.8), csmapi.ImageCoord(0.7, 6.6), csmapi.ImageCoord(1.5, 5.4), # csmapi.ImageCoord(2.3, 4.2), csmapi.ImageCoord(3.1, 4.9), csmapi.ImageCoord(5.8, 3.7), # csmapi.ImageCoord(6.6, 2.5), csmapi.ImageCoord(7.4, 1.3), csmapi.ImageCoord(8.2, 0.1)] sensors['a'].groundToImage.side_effect = [ csmapi.ImageCoord(-0.1, 7.8), csmapi.ImageCoord(5.8, 3.7) ] sensors['b'].groundToImage.side_effect = [ csmapi.ImageCoord(0.7, 6.6), csmapi.ImageCoord(3.1, 4.9), csmapi.ImageCoord(6.6, 2.5) ] sensors['c'].groundToImage.side_effect = [ csmapi.ImageCoord(1.5, 5.4), csmapi.ImageCoord(7.4, 1.3) ] sensors['d'].groundToImage.side_effect = [ csmapi.ImageCoord(2.3, 4.2), csmapi.ImageCoord(8.2, 0.1) ] V = bundle.compute_residuals(control_network, sensors) assert V.shape == (18, ) np.testing.assert_allclose(V, [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1 ])
def _(dem, image_pt, camera, max_its=20, tolerance=0.001): if not isinstance(image_pt, csmapi.ImageCoord): # Support a call where image_pt is in the form (x,y) image_pt = csmapi.ImageCoord(*image_pt) intersection = generate_ground_point(0.0, image_pt, camera) iterations = 0 semi_major, semi_minor = get_radii(camera) ecef = pyproj.Proj(proj='geocent', a=semi_major, b=semi_minor) lla = pyproj.Proj(proj='latlon', a=semi_major, b=semi_minor) while iterations != max_its: lon, lat, alt = pyproj.transform(ecef, lla, intersection.x, intersection.y, intersection.z) px, py = dem.latlon_to_pixel(lat, lon) height = dem.read_array(1, [px, py, 1, 1])[0][0] next_intersection = camera.imageToGround(image_pt, float(height)) dist = max(abs(intersection.x - next_intersection.x), abs(intersection.y - next_intersection.y), abs(intersection.z - next_intersection.z)) intersection = next_intersection iterations += 1 if dist < tolerance: break return intersection
def generate_ground_point(dem, image_pt, camera): ''' Generates a longitude, latitude, and altitude coordinate for a given x, y pixel coordinate Parameters ---------- dem : float or object Either a float that represents the height above the datum or a GeoDataset object generated from Plio off of a Digital Elevation Model (DEM) image_pt : tuple Pair of x, y coordinates in pixel space camera : object USGSCSM camera model object max_its : int, optional Maximum number of iterations to go through if the height does not converge tolerance : float, optional Number of decimal places to solve to when solving for height Returns ------- : object CSM EcefCoord containing the newly computed lon, lat, and alt values corresponding to the original image_pt coordinates ''' if not isinstance(image_pt, csmapi.ImageCoord): # Support a call where image_pt is in the form (x,y) image_pt = csmapi.ImageCoord(*image_pt) return camera.imageToGround(image_pt, dem)
def generate_gcps(camera, nnodes=5, semi_major=3396190, semi_minor=3376200): ecef = pyproj.Proj(proj='geocent', a=semi_major, b=semi_minor) lla = pyproj.Proj(proj='latlon', a=semi_major, b=semi_minor) isize = camera.getImageSize() isize = [isize.samp, isize.line] x = np.linspace(0, isize[1], 10) y = np.linspace(0, isize[0], 10) boundary = [(i,0.) for i in x] + [(isize[1], i) for i in y[1:]] +\ [(i, isize[0]) for i in x[::-1][1:]] + [(0.,i) for i in y[::-1][1:]] gnds = np.empty((len(boundary), 3)) for i, b in enumerate(boundary): gnd = camera.imageToGround(csmapi.ImageCoord(*b), 0) gnds[i] = [gnd.x, gnd.y, gnd.z] lons, lats, alts = pyproj.transform(ecef, lla, gnds[:, 0], gnds[:, 1], gnds[:, 2]) lla = np.vstack((lons, lats, alts)).T tr = zip(boundary, lla) gcps = [] for i, t in enumerate(tr): l = '<GCP Id="{}" Info="{}" Pixel="{}" Line="{}" X="{}" Y="{}" Z="{}" />'.format( i, i, t[0][1], t[0][0], t[1][0], t[1][1], t[1][2]) gcps.append(l) return gcps
def generate_latlon_boundary(camera, nnodes=5, semi_major=3396190, semi_minor=3376200, n_points=10): ''' Generates a latlon bounding box given a camera model Parameters ---------- camera : object csmapi generated camera model nnodes : int Not sure semi_major : int Semimajor axis of the target body semi_minor : int Semiminor axis of the target body n_points : int Number of points to generate between the corners of the bounding box per side. Returns ------- lons : list List of longitude values lats : list List of latitude values alts : list List of altitude values ''' isize = camera.getImageSize() isize = (isize.line, isize.samp) boundary = generate_boundary(isize, nnodes=nnodes, n_points=n_points) ecef = pyproj.Proj(proj='geocent', a=semi_major, b=semi_minor) lla = pyproj.Proj(proj='latlon', a=semi_major, b=semi_minor) gnds = np.empty((len(boundary), 3)) for i, b in enumerate(boundary): # Could be potential errors or warnings from imageToGround try: gnd = camera.imageToGround(csmapi.ImageCoord(*b), 0) except: pass gnds[i] = [gnd.x, gnd.y, gnd.z] lons, lats, alts = pyproj.transform(ecef, lla, gnds[:, 0], gnds[:, 1], gnds[:, 2]) return lons, lats, alts
def triangulate_ground_pt(cameras, image_pts): """ Given a set of cameras and image points, find the ground point closest to the image rays for the image points. This function minimizes the sum of the squared distances from the ground point to the image rays. Parameters ---------- cameras : list A list of CSM compliant sensor model objects image_pts : list A list of x, y image point tuples Returns ------- : tuple The ground point as an (x, y, z) tuple """ if len(cameras) != len(image_pts): raise ValueError( "Lengths of cameras ({}) and image_pts ({}) must be the " "same".format(len(cameras), len(image_pts))) M = np.zeros((3, 3)) b = np.zeros(3) unit_x = np.array([1, 0, 0]) unit_y = np.array([0, 1, 0]) unit_z = np.array([0, 0, 1]) for camera, image_pt in zip(cameras, image_pts): if not isinstance(image_pt, csmapi.ImageCoord): image_pt = csmapi.ImageCoord(*image_pt) locus = camera.imageToRemoteImagingLocus(image_pt) look = np.array( [locus.direction.x, locus.direction.y, locus.direction.z]) pos = np.array([locus.point.x, locus.point.y, locus.point.z]) look_squared = np.dot(look, look) M[0] += look[0] * look - look_squared * unit_x M[1] += look[1] * look - look_squared * unit_y M[2] += look[2] * look - look_squared * unit_z b += np.dot(pos, look) * look - look_squared * pos return tuple(np.dot(np.linalg.inv(M), b))
def generate_latlon_footprint(camera, nnodes=5, semi_major=3396190, semi_minor=3376200): ecef = pyproj.Proj(proj='geocent', a=semi_major, b=semi_minor) lla = pyproj.Proj(proj='latlon', a=semi_major, b=semi_minor) isize = camera.getImageSize() isize = [isize.samp, isize.line] x = np.linspace(0, isize[1], 10) y = np.linspace(0, isize[0], 10) multipoly = ogr.Geometry(ogr.wkbMultiPolygon) boundary = [(i,0.) for i in x] + [(isize[1], i) for i in y[1:]] +\ [(i, isize[0]) for i in x[::-1][1:]] + [(0.,i) for i in y[::-1][1:]] ring = ogr.Geometry(ogr.wkbLinearRing) for i in boundary: gnd = camera.imageToGround(csmapi.ImageCoord(*i), 0) lons, lats, alts = pyproj.transform(ecef, lla, gnd.x, gnd.y, gnd.z) ring.AddPoint(lons, lats) poly = ogr.Geometry(ogr.wkbPolygon) poly.AddGeometry(ring) multipoly.AddGeometry(poly) return multipoly
def func(row, args): camera = args[0] imagecoord = csmapi.ImageCoord(float(row[1]), float(row[0])) # An elevation at the ellipsoid is plenty accurate for this work gnd = getattr(camera, 'imageToGround')(imagecoord, 0) return [gnd.x, gnd.y, gnd.z]