コード例 #1
0
def inf_zero_one_to_triple(p, q, r):
    """returns SL(2,C) matrix that sends the three points infinity, zero, one to given input points p,q,r"""
    ### infinity = [1,0], zero = [0,1], one = [1,1] in CP^1
    p1, p2 = p
    q1, q2 = q
    r1, r2 = r
    M = [[p1, q1], [p2, q2]]
    Minv = matrix2_inv(M)
    [mu, lam] = matrix_mult_vector(matrix2_inv([[p1, q1], [p2, q2]]), [r1, r2])
    return [[mu * p1, lam * q1], [mu * p2, lam * q2]]
コード例 #2
0
def inf_zero_one_to_triple(p,q,r):
  """returns SL(2,C) matrix that sends the three points infinity, zero, one to given input points p,q,r"""
  ### infinity = [1,0], zero = [0,1], one = [1,1] in CP^1
  p1,p2=p
  q1,q2=q
  r1,r2=r
  M = [[p1,q1],[p2,q2]]
  Minv = matrix2_inv(M)
  [mu,lam] = matrix_mult_vector(matrix2_inv([[p1,q1],[p2,q2]]), [r1,r2])
  return [[mu*p1, lam*q1],[mu*p2, lam*q2]]
コード例 #3
0
def apply_SL2C_elt_to_image(M,
                            source_image_filename,
                            out_x_size=None,
                            save_filename="sphere_transforms_test.png"):
    """apply an element of SL(2,C) (i.e. a matrix) to an equirectangular image file"""
    Minv = matrix2_inv(
        M
    )  # to push an image forwards by M, we pull the pixel coordinates of the output backwards
    source_image = Image.open(source_image_filename)
    s_im = source_image.load()  # faster pixel by pixel access
    in_x_size, in_y_size = source_image.size
    if out_x_size == None:
        out_x_size, out_y_size = source_image.size
    else:
        out_y_size = out_x_size / 2
    out_image = Image.new("RGB", (out_x_size, out_y_size))
    o_im = out_image.load()  # faster pixel by pixel access

    for i in range(out_x_size):
        for j in range(out_y_size):
            pt = (i, j)
            pt = angles_from_pixel_coords(pt, x_size=out_x_size)
            pt = sphere_from_angles(pt)
            pt = CP1_from_sphere(pt)
            pt = matrix_mult_vector(Minv, pt)
            pt = sphere_from_CP1(pt)
            pt = angles_from_sphere(pt)
            pt = pixel_coords_from_angles(pt, x_size=in_x_size)
            o_im[i, j] = get_interpolated_pixel_colour(pt,
                                                       s_im,
                                                       x_size=in_x_size)
    out_image.save(save_filename)
コード例 #4
0
def zoom_in_on_pixel_coords(p, zoom_factor, x_size=1000):
    """p is pixel coordinate point, return SL(2,C) matrix zooming in on p by a factor of scale"""
    # Note that the zoom factor is only accurate at the point p itself. As we move away from p, we zoom less and less.
    # We zoom with the inverse zoom_factor towards/away from the antipodal point to p.
    M_rot = rotate_pixel_coords_p_to_q(p, (0, 0), x_size=x_size)
    M_scl = [[zoom_factor, 0], [0, 1]]  ### zoom in on zero in CP^1
    return matrix_mult(matrix_mult(matrix2_inv(M_rot), M_scl), M_rot)
コード例 #5
0
def apply_SL2C_elt_to_image(M, source_image_filename, out_x_size = None, save_filename = "sphere_transforms_test.png"):
  """apply an element of SL(2,C) (i.e. a matrix) to an equirectangular image file"""
  Minv = matrix2_inv(M)  # to push an image forwards by M, we pull the pixel coordinates of the output backwards 
  source_image = Image.open(source_image_filename)
  s_im = source_image.load()  # faster pixel by pixel access 
  in_x_size, in_y_size = source_image.size
  if out_x_size == None:
    out_x_size, out_y_size = source_image.size
  else:
    out_y_size = out_x_size/2
  out_image = Image.new("RGB", (out_x_size, out_y_size))
  o_im = out_image.load()  # faster pixel by pixel access 

  for i in range(out_x_size): 
    for j in range(out_y_size):
      pt = (i,j)
      pt = angles_from_pixel_coords(pt, x_size = out_x_size)
      pt = sphere_from_angles(pt)
      pt = CP1_from_sphere(pt)
      pt = matrix_mult_vector(Minv, pt)
      pt = sphere_from_CP1(pt)
      pt = angles_from_sphere(pt)
      pt = pixel_coords_from_angles(pt, x_size = in_x_size)
      o_im[i,j] = get_interpolated_pixel_colour(pt, s_im, x_size = in_x_size)
  out_image.save(save_filename)
コード例 #6
0
def zoom_in_on_pixel_coords(p, zoom_factor, x_size = 1000):
  """p is pixel coordinate point, return SL(2,C) matrix zooming in on p by a factor of scale"""
  # Note that the zoom factor is only accurate at the point p itself. As we move away from p, we zoom less and less.
  # We zoom with the inverse zoom_factor towards/away from the antipodal point to p.
  M_rot = rotate_pixel_coords_p_to_q( p, (0,0), x_size = x_size)
  M_scl = [[zoom_factor,0],[0,1]] ### zoom in on zero in CP^1
  return matrix_mult( matrix_mult(matrix2_inv(M_rot), M_scl), M_rot )
コード例 #7
0
def zoom_along_axis_sphere_points_p_q(p, q, zoom_factor):
  """p and q are points on sphere, return SL(2,C) matrix zooming along axis from p to q"""
  p, q = vector(p), vector(q)
  CP1p, CP1q = CP1_from_sphere(p), CP1_from_sphere(q)
  assert dot(p,q) < 0.9999   #points should not be in the same place
  r = get_vector_perp_to_p_and_q(p, q)
  CP1r = CP1_from_sphere(r)
  M_standardise = two_triples_to_SL(CP1p, CP1q, CP1r, [0,1], [1,0], [1,1])
  M_theta = [[zoom_factor,0],[0,1]] 
  return matrix_mult( matrix_mult(matrix2_inv(M_standardise), M_theta), M_standardise )
コード例 #8
0
def rotate_around_axis_sphere_points_p_q(p,q,theta):
  """p and q are points on sphere, return SL(2,C) matrix rotating by angle theta around the axis from p to q"""
  p, q = vector(p), vector(q)
  CP1p, CP1q = CP1_from_sphere(p), CP1_from_sphere(q)
  assert dot(p,q) < 0.9999, "axis points should not be in the same place!"
  r = get_vector_perp_to_p_and_q(p, q)
  CP1r = CP1_from_sphere(r)
  M_standardise = two_triples_to_SL(CP1p, CP1q, CP1r, [0,1], [1,0], [1,1])
  M_theta = [[complex(cos(theta),sin(theta)),0],[0,1]] #rotate on axis through 0, infty by theta
  return matrix_mult( matrix_mult(matrix2_inv(M_standardise), M_theta), M_standardise )
コード例 #9
0
def zoom_along_axis_sphere_points_p_q(p, q, zoom_factor):
    """p and q are points on sphere, return SL(2,C) matrix zooming along axis from p to q"""
    p, q = vector(p), vector(q)
    CP1p, CP1q = CP1_from_sphere(p), CP1_from_sphere(q)
    assert dot(p, q) < 0.9999  #points should not be in the same place
    r = get_vector_perp_to_p_and_q(p, q)
    CP1r = CP1_from_sphere(r)
    M_standardise = two_triples_to_SL(CP1p, CP1q, CP1r, [0, 1], [1, 0], [1, 1])
    M_theta = [[zoom_factor, 0], [0, 1]]
    return matrix_mult(matrix_mult(matrix2_inv(M_standardise), M_theta),
                       M_standardise)
コード例 #10
0
def rotate_around_axis_sphere_points_p_q(p, q, theta):
    """p and q are points on sphere, return SL(2,C) matrix rotating by angle theta around the axis from p to q"""
    p, q = vector(p), vector(q)
    CP1p, CP1q = CP1_from_sphere(p), CP1_from_sphere(q)
    assert dot(p, q) < 0.9999, "axis points should not be in the same place!"
    r = get_vector_perp_to_p_and_q(p, q)
    CP1r = CP1_from_sphere(r)
    M_standardise = two_triples_to_SL(CP1p, CP1q, CP1r, [0, 1], [1, 0], [1, 1])
    M_theta = [[complex(cos(theta), sin(theta)), 0],
               [0, 1]]  #rotate on axis through 0, infty by theta
    return matrix_mult(matrix_mult(matrix2_inv(M_standardise), M_theta),
                       M_standardise)
コード例 #11
0
def droste_effect(zoom_center_pixel_coords, zoom_factor, zoom_cutoff, source_image_filename, out_x_size = None, twist = False, zoom_loop_value = 0.0, save_filename = "sphere_transforms_test.png"):
  """produces a zooming Droste effect image from an equirectangular source image"""
  # The source image should be a composite of the original image together with a zoomed version, 
  # fit inside a picture frame or similar in the original image
  source_image = Image.open(source_image_filename)
  s_im = source_image.load()
  in_x_size, in_y_size = source_image.size
  
  M_rot = rotate_pixel_coords_p_to_q(zoom_center_pixel_coords, (0,0), x_size = in_x_size)
  M_rot_inv = matrix2_inv(M_rot)
  if out_x_size == None:
    out_x_size, out_y_size = source_image.size
  else:
    out_y_size = out_x_size/2
  out_image = Image.new("RGB", (out_x_size, out_y_size))
  o_im = out_image.load()

  droste_factor = ( cmath.log(zoom_factor) + complex(0, 2*pi) ) / complex(0, 2*pi)  # used if twisting 

  for i in range(out_x_size): 
    for j in range(out_y_size):
      pt = (i,j)
      pt = angles_from_pixel_coords(pt, x_size = out_x_size)
      pt = sphere_from_angles(pt)
      pt = CP1_from_sphere(pt)
      pt = matrix_mult_vector(M_rot, pt)

      # if ever you don't know how to do some operation in complex projective coordinates, it's almost certainly 
      # safe to just switch back to ordinary complex numbers by pt = pt[0]/pt[1]. The only danger is if pt[1] == 0, 
      # or is near enough to cause floating point errors. In this application, you are probably fine unless you 
      # make some very specific choices of where to zoom to etc. 
      pt = pt[0]/pt[1]  
      pt = cmath.log(pt)
      if twist:  # otherwise straight zoom
        pt = droste_factor * pt  

      # zoom_loop_value is between 0 and 1, vary this from 0.0 to 1.0 to animate frames zooming into the droste image
      pt = complex(pt.real + log(zoom_factor) * zoom_loop_value, pt.imag) 
      
      # zoom_cutoff alters the slice of the input image that we use, so that it covers mostly the original image, together with 
      # some of the zoomed image that was composited with the original. The slice needs to cover the seam between the two
      # (i.e. the picture frame you are using, but should cover as little as possible of the zoomed version of the image.
      pt = complex((pt.real + zoom_cutoff) % log(zoom_factor) - zoom_cutoff, pt.imag) 
      pt = cmath.exp(pt)
      pt = [pt, 1] #back to projective coordinates
      pt = matrix_mult_vector(M_rot_inv, pt)
      pt = sphere_from_CP1(pt)
      pt = angles_from_sphere(pt)
      pt = pixel_coords_from_angles(pt, x_size = in_x_size)
      o_im[i,j] = get_interpolated_pixel_colour(pt, s_im, in_x_size)

  out_image.save(save_filename)
コード例 #12
0
def droste_effect(zoom_center_pixel_coords,
                  zoom_factor,
                  zoom_cutoff,
                  source_image_filename,
                  out_x_size=None,
                  twist=False,
                  zoom_loop_value=0.0,
                  save_filename="sphere_transforms_test.png"):
    """produces a zooming Droste effect image from an equirectangular source image"""
    # The source image should be a composite of the original image together with a zoomed version,
    # fit inside a picture frame or similar in the original image
    source_image = Image.open(source_image_filename)
    s_im = source_image.load()
    in_x_size, in_y_size = source_image.size

    M_rot = rotate_pixel_coords_p_to_q(zoom_center_pixel_coords, (0, 0),
                                       x_size=in_x_size)
    M_rot_inv = matrix2_inv(M_rot)
    if out_x_size == None:
        out_x_size, out_y_size = source_image.size
    else:
        out_y_size = out_x_size / 2
    out_image = Image.new("RGB", (out_x_size, out_y_size))
    o_im = out_image.load()

    droste_factor = (cmath.log(zoom_factor) + complex(0, 2 * pi)) / complex(
        0, 2 * pi)  # used if twisting

    for i in range(out_x_size):
        for j in range(out_y_size):
            pt = (i, j)
            pt = angles_from_pixel_coords(pt, x_size=out_x_size)
            pt = sphere_from_angles(pt)
            pt = CP1_from_sphere(pt)
            pt = matrix_mult_vector(M_rot, pt)

            # if ever you don't know how to do some operation in complex projective coordinates, it's almost certainly
            # safe to just switch back to ordinary complex numbers by pt = pt[0]/pt[1]. The only danger is if pt[1] == 0,
            # or is near enough to cause floating point errors. In this application, you are probably fine unless you
            # make some very specific choices of where to zoom to etc.
            pt = pt[0] / pt[1]
            pt = cmath.log(pt)
            if twist:  # otherwise straight zoom
                pt = droste_factor * pt

            # zoom_loop_value is between 0 and 1, vary this from 0.0 to 1.0 to animate frames zooming into the droste image
            pt = complex(pt.real + log(zoom_factor) * zoom_loop_value, pt.imag)

            # zoom_cutoff alters the slice of the input image that we use, so that it covers mostly the original image, together with
            # some of the zoomed image that was composited with the original. The slice needs to cover the seam between the two
            # (i.e. the picture frame you are using, but should cover as little as possible of the zoomed version of the image.
            pt = complex(
                (pt.real + zoom_cutoff) % log(zoom_factor) - zoom_cutoff,
                pt.imag)
            pt = cmath.exp(pt)
            pt = [pt, 1]  #back to projective coordinates
            pt = matrix_mult_vector(M_rot_inv, pt)
            pt = sphere_from_CP1(pt)
            pt = angles_from_sphere(pt)
            pt = pixel_coords_from_angles(pt, x_size=in_x_size)
            o_im[i, j] = get_interpolated_pixel_colour(pt, s_im, in_x_size)

    out_image.save(save_filename)
コード例 #13
0
def two_triples_to_SL(a1, b1, c1, a2, b2, c2):
    """returns SL(2,C) matrix that sends the three CP^1 points a1,b1,c1 to a2,b2,c2"""
    return matrix_mult(inf_zero_one_to_triple(a2, b2, c2),
                       matrix2_inv(inf_zero_one_to_triple(a1, b1, c1)))
コード例 #14
0
def two_triples_to_SL(a1,b1,c1,a2,b2,c2):
  """returns SL(2,C) matrix that sends the three CP^1 points a1,b1,c1 to a2,b2,c2"""
  return matrix_mult( inf_zero_one_to_triple(a2,b2,c2), matrix2_inv(inf_zero_one_to_triple(a1,b1,c1) ) )