def mercator2ortho(mercator_image_size, latitude, angular_width, out_width): phi0 = latitude #latitude and longitude of the sin_phi0 = sin(phi0) cos_phi0 = cos(phi0) z0 = cos_phi0 x0 = sin_phi0 #y0 = 0 #Absolute coordinate of the point on the sphere swidth, sheight = mercator_image_size out_height = int(sheight / swidth * out_width) y_merc0 = asinh(tan(phi0)) def ortho2merc_tfm(xp, yp): r2 = xp**2 + yp**2 if r2 > 1: return None #Out of domain zp = sqrt(1 - r2) #XYZ are coordinates in the rotated coordinate system #Rotate them by phi (perpendicular to equator), in the xz plane x,z = zp * cos_phi0 - yp * sin_phi0, \ zp * sin_phi0 + yp * cos_phi0 y = xp #now (x, y, z) are global coordinates of the projected point on a sphere #Convert them back to angles... r_xy = sqrt(x**2 + y**2) #phi2 = atan2( z, r_xy ) if r_xy == 0: return None return ( atan2(y, x), #lambda2 asinh(z / r_xy) - y_merc0) #asinh(tan(phi2)) #angular size of 1 pixel src_pixel_size = angular_width / swidth #Determine the size of the projected map piece, in the units where Earth radius = 1. projection_width = orthogonal_projection_width(mercator_image_size, latitude, angular_width) # dst_pixel_size = angular_width / out_width dst_pixel_size = projection_width / out_width scaled_ortho2merc_tfm = compose( translate_tfm(swidth * 0.5, sheight * 0.5), scale_tfm(-1.0 / src_pixel_size), ortho2merc_tfm, scale_tfm(-dst_pixel_size), translate_tfm(-out_width * 0.5, -out_height * 0.5)) return (out_width, out_height), scaled_ortho2merc_tfm, dst_pixel_size
def inv_logpolar_transform(image_size, y0, out_width, out_height, alpha0 = 0): """Inverse log polar transform """ xc = out_width/2 yc = out_height/2 center = (xc,yc) swidth, sheight = image_size log_rmax = log(xc**2+yc**2)*0.5 #source image scale (pixels per unit) # image width must be 2PI units source_scale = (swidth-1)/2/pi def logz(xf,yf): if xf == 0 and yf == 0: return 0, 1e-2 else: return atan2(yf, xf), log(xf*xf+yf*yf)*0.5 tfm_func1 = compose( #without translate, min y is: -log_rmax*source_scale. It must be y0. translate_tfm( source_scale*pi, log_rmax*source_scale+y0 ), scale_tfm( source_scale, -source_scale ), logz, translate_tfm(-xc, -yc) ) return tfm_func1
def logpolar_transform(image_size, center, out_width=None, out_height=None, alpha0 = 0): swidth, sheight = image_size if center is None: center = (swidth/2, sheight/2) if out_width is None: #Automatically determine output width #Should be enough for mostly loseless transform out_width = (swidth+sheight)*2 x0,y0 = center min_log = log(0.5) max_log = log(max(x0, swidth-x0)**2 + max(y0,sheight-y0)**2)*0.5 #Determine appropriate height of the output image, requiring that whole original # image fits into it, and highest zoom levels is single pixel. if out_height is None: out_height = int(out_width / (2*pi) * (max_log-min_log)) out_size = (out_width, out_height) out_scale = 2*pi/out_width def expz(xf,yf): ey = exp(yf) return cos(xf)*ey, sin(xf)*ey tfm_func1 = compose( translate_tfm(x0, y0), expz, translate_tfm( alpha0, max_log ), scale_tfm( out_scale, -out_scale) ) def tfm_func(x,y): xf = x*out_scale + alpha0 yf = max_log-y*out_scale #Put highest resolution at the top ey = exp(yf) yfs = sin(xf)*ey xfs = cos(xf)*ey return xfs + x0, yfs + y0 return (out_width, out_height), tfm_func
def logpolar_transform(image_size, center, out_width=None, out_height=None, alpha0=0): swidth, sheight = image_size if center is None: center = (swidth / 2, sheight / 2) if out_width is None: #Automatically determine output width #Should be enough for mostly loseless transform out_width = (swidth + sheight) * 2 x0, y0 = center min_log = log(0.5) max_log = log(max(x0, swidth - x0)**2 + max(y0, sheight - y0)**2) * 0.5 #Determine appropriate height of the output image, requiring that whole original # image fits into it, and highest zoom levels is single pixel. if out_height is None: out_height = int(out_width / (2 * pi) * (max_log - min_log)) out_size = (out_width, out_height) out_scale = 2 * pi / out_width def expz(xf, yf): ey = exp(yf) return cos(xf) * ey, sin(xf) * ey tfm_func1 = compose(translate_tfm(x0, y0), expz, translate_tfm(alpha0, max_log), scale_tfm(out_scale, -out_scale)) def tfm_func(x, y): xf = x * out_scale + alpha0 yf = max_log - y * out_scale #Put highest resolution at the top ey = exp(yf) yfs = sin(xf) * ey xfs = cos(xf) * ey return xfs + x0, yfs + y0 return (out_width, out_height), tfm_func
def download_and_glue(coordinates, zoom_range=(0, 19), fragment_size=(512, 512), out_width=1024, alpha_gradient_size=10, map_type="roadmap", mercator_to_ortho=True, mesh_step=8, scale=2, margins=(0, 0, 0, 0)): #Increasing zoom by one level offsets image by this amount in the logarithmic view zoom_level_offset = (0.5 * log(2) / pi) * out_width fragment_size_scaled = tuple(s * scale for s in fragment_size) #Prepare alpha alpha = make_alpha(fragment_size_scaled, alpha_gradient_size, margins) z0, z1 = zoom_range out_height = int(zoom_level_offset * (z1 - z0 + 1)) print("Output image size: {out_width}x{out_height}".format(**locals())) out_image = Image.new("RGBA", (out_width, out_height)) dy_base = None for zoom in range(z0, z1 + 1): print("Downloading fragment, zoom={zoom}... ".format(**locals()), end='', flush=True) fragment = get_map_cached(coordinates, zoom, fragment_size, map_type, scale) fragment.putalpha(alpha) print("done, downloaded size: {fragment.size}".format(**locals())) if not mercator_to_ortho: dy = (zoom - z0) * zoom_level_offset _, tfm = logpolar_transform(fragment.size, scale_tfm(0.5)(*fragment.size), out_width=out_width) else: longitude_extent = (2 * pi) * fragment.size[0] / 256 / scale * ( 0.5)**zoom #Make a transform from mercator to orthogonal out_ortho_size, merc2otrho_tfm, ortho_pix_size = mercator2ortho( fragment.size, coordinates[0] / 180 * pi, #latitude longitude_extent, out_width) _, log_tfm = logpolar_transform( out_ortho_size, center=scale_tfm(0.5)(*out_ortho_size), out_width=out_width) if dy_base is None: dy_base = (0.5 / pi * log(ortho_pix_size)) * out_width dy = 0 else: dy = dy_base - (0.5 / pi * log(ortho_pix_size)) * out_width tfm = compose(merc2otrho_tfm, log_tfm) transformed_size = (out_width, int(zoom_level_offset * 3)) #Put transformed image to the output print(" Transforming fragment...", end='', flush=True) transformed = transform_image(fragment, tfm, transformed_size, mesh_step=mesh_step) print("Done, created {transformed.size} image.".format(**locals())) paste_with_alpha(out_image, transformed, (0, int(dy))) print("Done") return out_image
def download_and_glue(coordinates, zoom_range=(0,19), fragment_size=(512,512), out_width = 1024, alpha_gradient_size=10, map_type="roadmap", mercator_to_ortho=True, mesh_step=8, scale=2, margins=(0,0,0,0)): #Increasing zoom by one level offsets image by this amount in the logarithmic view zoom_level_offset = (0.5*log(2)/pi)*out_width fragment_size_scaled = tuple(s*scale for s in fragment_size) #Prepare alpha alpha = make_alpha(fragment_size_scaled, alpha_gradient_size, margins) z0, z1 = zoom_range out_height = int(zoom_level_offset * (z1-z0+1)) print ("Output image size: {out_width}x{out_height}".format(**locals())) out_image = Image.new("RGBA",(out_width, out_height)) dy_base = None for zoom in range(z0,z1+1): print ("Downloading fragment, zoom={zoom}... ".format(**locals()), end='', flush=True) fragment = get_map_cached(coordinates, zoom, fragment_size, map_type, scale) fragment.putalpha(alpha) print ("done, downloaded size: {fragment.size}".format(**locals())) if not mercator_to_ortho: dy = (zoom-z0) * zoom_level_offset _, tfm = logpolar_transform( fragment.size, scale_tfm(0.5)(*fragment.size), out_width=out_width) else: longitude_extent = (2*pi)*fragment.size[0]/256/scale*(0.5)**zoom #Make a transform from mercator to orthogonal out_ortho_size, merc2otrho_tfm, ortho_pix_size = mercator2ortho( fragment.size, coordinates[0]/180*pi, #latitude longitude_extent, out_width ) _, log_tfm = logpolar_transform( out_ortho_size, center = scale_tfm(0.5)(*out_ortho_size), out_width=out_width ) if dy_base is None: dy_base = (0.5/pi*log(ortho_pix_size))*out_width dy = 0 else: dy = dy_base - (0.5/pi*log(ortho_pix_size))*out_width tfm = compose( merc2otrho_tfm, log_tfm ) transformed_size = (out_width, int(zoom_level_offset*3)) #Put transformed image to the output print(" Transforming fragment...", end='', flush=True) transformed=transform_image(fragment, tfm, transformed_size, mesh_step=mesh_step) print("Done, created {transformed.size} image.".format(**locals())) paste_with_alpha(out_image, transformed, (0, int(dy))) print ("Done") return out_image
def main(): from optparse import OptionParser parser = OptionParser(usage = "%prog [options] INPUT_IMAGE OUTPUT_IMAGE\n" "Log-Polar image transform. Generated image always have RGBA format") parser.add_option("-c", "--center", dest="center", help="Center point position, x:y", metavar="X:Y") parser.add_option("-A", "--angle", dest="angle", type=float, default=0.0, help="Angle, corresponding left side of the transformed image, in graduses. 0 is horizontal, left to right.", metavar="ANGLE") parser.add_option("-w", "--width", dest="width", type=int, help="Width of the output image. Default is auto-detect, based on the source inmage dimensions (the size is usually quite big)", metavar="PIXELS") parser.add_option("-H", "--height", dest="height", type=int, help="Height of the output image. Default is auto-detect, based on width", metavar="PIXELS") parser.add_option("", "--mesh-step", dest="mesh_step", type=int, default=8, help="Step of the output mesh. Default is 8", metavar="PIXELS") parser.add_option("", "--mercator2ortho", dest="mercator2ortho", help="Treat source image as a piece of the map in Mercator projection. Map in converted to orthogonal projection regarding the point in the center of the map.", metavar="CENTER_LAT:LNG_WIDTH") (options, args) = parser.parse_args() if len(args) < 1: parser.error("No input file specified") input =args[0] if len(args) >= 2: output = args[1] else: output = None if options.mercator2ortho: if options.center: parser.error("Center not supported in mercator map pieces. It is always at the center of the image") try: lat_center_s, lng_extent_s = options.mercator2ortho.split(":",2) mercator2ortho_options = { "center_lat": float(lat_center_s)/180*pi, "lng_extent": float(lng_extent_s)/180*pi } except Exception as err: parser.error("Error parsing mercator projection options: {0}".format(err)) else: mercator2ortho_options = None if options.center is None: center = None else: center = tuple(map(int, options.center.split(":",2))) img = Image.open(input) #Image conversions # SOurce image can be one of: # - RGB # - RGBA - has alpha # - I - may have alpha # - L # - 1 # Target image: # always have alpha. if img.mode != "RGBA": img =img.convert("RGBA") if mercator2ortho_options: from mercator2ortho import mercator2ortho out_ortho_size, merc2otrho_tfm, _ = mercator2ortho(img.size, mercator2ortho_options["center_lat"], mercator2ortho_options["lng_extent"], max(img.size) ) out_size, ortho2log_tfm = logpolar_transform(out_ortho_size, center=scale_tfm(0.5)(*out_ortho_size), out_width = options.width, alpha0 = options.angle/180*pi) #Create composite transform: first convert Mercator map to orthogonal projection, then apply log-transform to it. transform = compose( merc2otrho_tfm, ortho2log_tfm ) else: out_size, transform = logpolar_transform(img.size, center=center, out_width = options.width, alpha0 = options.angle/180*pi) img = transform_image(img, transform, out_size, mesh_step=options.mesh_step) if output: img.save(output) else: img.show()
def main(): from optparse import OptionParser parser = OptionParser( usage="%prog [options] INPUT_IMAGE OUTPUT_IMAGE\n" "Log-Polar image transform. Generated image always have RGBA format") parser.add_option("-c", "--center", dest="center", help="Center point position, x:y", metavar="X:Y") parser.add_option( "-A", "--angle", dest="angle", type=float, default=0.0, help= "Angle, corresponding left side of the transformed image, in graduses. 0 is horizontal, left to right.", metavar="ANGLE") parser.add_option( "-w", "--width", dest="width", type=int, help= "Width of the output image. Default is auto-detect, based on the source inmage dimensions (the size is usually quite big)", metavar="PIXELS") parser.add_option( "-H", "--height", dest="height", type=int, help= "Height of the output image. Default is auto-detect, based on width", metavar="PIXELS") parser.add_option("", "--mesh-step", dest="mesh_step", type=int, default=8, help="Step of the output mesh. Default is 8", metavar="PIXELS") parser.add_option( "", "--mercator2ortho", dest="mercator2ortho", help= "Treat source image as a piece of the map in Mercator projection. Map in converted to orthogonal projection regarding the point in the center of the map.", metavar="CENTER_LAT:LNG_WIDTH") (options, args) = parser.parse_args() if len(args) < 1: parser.error("No input file specified") input = args[0] if len(args) >= 2: output = args[1] else: output = None if options.mercator2ortho: if options.center: parser.error( "Center not supported in mercator map pieces. It is always at the center of the image" ) try: lat_center_s, lng_extent_s = options.mercator2ortho.split(":", 2) mercator2ortho_options = { "center_lat": float(lat_center_s) / 180 * pi, "lng_extent": float(lng_extent_s) / 180 * pi } except Exception as err: parser.error( "Error parsing mercator projection options: {0}".format(err)) else: mercator2ortho_options = None if options.center is None: center = None else: center = tuple(map(int, options.center.split(":", 2))) img = Image.open(input) #Image conversions # SOurce image can be one of: # - RGB # - RGBA - has alpha # - I - may have alpha # - L # - 1 # Target image: # always have alpha. if img.mode != "RGBA": img = img.convert("RGBA") if mercator2ortho_options: from mercator2ortho import mercator2ortho out_ortho_size, merc2otrho_tfm, _ = mercator2ortho( img.size, mercator2ortho_options["center_lat"], mercator2ortho_options["lng_extent"], max(img.size)) out_size, ortho2log_tfm = logpolar_transform( out_ortho_size, center=scale_tfm(0.5)(*out_ortho_size), out_width=options.width, alpha0=options.angle / 180 * pi) #Create composite transform: first convert Mercator map to orthogonal projection, then apply log-transform to it. transform = compose(merc2otrho_tfm, ortho2log_tfm) else: out_size, transform = logpolar_transform(img.size, center=center, out_width=options.width, alpha0=options.angle / 180 * pi) img = transform_image(img, transform, out_size, mesh_step=options.mesh_step) if output: img.save(output) else: img.show()