Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transform and remap an equirectangular image with a 90° roll

I have to transform and remap an equirectangular image to an other equirectangular image with a 90° roll.

I did it with Pano2VR.

The problem is that I have to do it programmatically from the server side. So I can't use a G.U.I. to do it.

First, I oriented my research to imagemagick. I tried the Fred ImageMagick scripts but could not find anyone to do what I want to do. Moreover, the processing time of an image appears very long compared to Pano2VR.

I directed my investigations to OpenCV and the libgnomonic. It's presently the most interesting way. This library allows te user to transform projections (equirectangular to rectilinear and vice versa) or make equirectangular mapping transformation. I played with Norama-suite wich contains some scripts to deal with the library. For example, I would to convert an rectilinear image to an equirectangular but the output was just a black background image (why ? I didn't find the answer).

However, this second link could resolve my problem. I have this image :

and I want to transform it to this image

Well, I'm not comfortable at all with C. I think I should use this two files :

  • https://www.github.com/FoxelSA/libgnomonic/blob/master/src/gnomonic-transform.h
  • https://www.github.com/FoxelSA/libgnomonic/blob/master/src/gnomonic-transform.c

But I don't know how. And above all, I want to understand.

Am I on the right way ? What transformation is applied on the first image ? Is there a way to do it with python or a bash script ?

Well, thank you for your help.


**EDIT Transposition of C in python ** The following code didn't work and return and IndexError. However I tried to catch and pass the exception and the first right part of the image did not seem changed.

import math
from PIL import Image

img = Image.open("img1.jpg")
img = img.convert('RGB')
pixel = img.load()
width, height = img.size

img2 = img.copy()
for y in xrange(height):
    for x in xrange(width):
        xx = 2*(y+0.5) / width - 1.0
        yy = 2*(y+0.5)/ height - 1.0
        lng = math.pi * xx
        lat = 0.5 * math.pi * yy

        # NOTE!  These axes are transposed because that's what the question is about
        Z = math.cos(lat) * math.cos(lng)  # normally X
        Y = math.cos(lat) * math.sin(lng)  # normally Y
        X = -math.sin(lat)                 # normally -Z
        D = math.sqrt(X*X+Y*Y)

        lat = math.atan2(Z, D)             # ? normally lat = math.asin(Z)
        lng = math.atan2(Y, X)

        #ix and iy must be integers
        ix = int((0.5 * lng / math.pi + 0.5) * width - 0.5)
        iy = int((lat/math.pi + 0.5) * height  - 0.5)

        #not sure of this part to remap the image
        newpixel = pixel[ix, iy]
        img2.putpixel([(x+width/4) % width, y], newpixel)
        #I tries as mentionned in the following code to invert x and y in the two previous lines but the index error out of range comes back 
img2.show()
like image 763
pep Avatar asked Feb 05 '16 19:02

pep


1 Answers

Your transformation has two steps. The first step is a transformation of the projection sphere, the second is the 90° roll.

A 90° roll of an equirectangular image is just a horizontal shift of a quarter of the image width. The first transformation is more complicated: You basically want to rotate the sphere so that the north pole is at latitude 0 and longitude 0 (somewhere in the Gulf of Guinea, if you take thze Earth as reference.)

You can go about this transformation with these steps;

  • Translate the x and y position of each pixel into a longitude, −π ≤ long ≤ π, and a latitude, −π/2 ≤ lat ≤ π/2. This is a linear transformation.
  • Create x, y, and z coordinates of the corresponding longitude and latitude on a unit sphere; positive z is the north pole.
  • Rotate these Cartesian coordinates. In your case, you just have to swap some dimensions, but this could be a general transformation with any transformation matrix.
  • Calculate the longituide and latitude of the rotated coordinates.
  • Transform the new longitude and latitude to a pixel position.

Here's C code that works. (I know that you have tagged the question with Python, but the code below is mostly formulas that work similarly in python. You have to take care: All of the number are floating point numbers except the pixel indixes x, y, ixand iy. I would have done this in Python, but I have no experience with the Python Image Library.)

for (y = 0; y < height; y++) {
    for (x = 0; x < width; x++) {
        double xx = 2 * (x + 0.5) / width - 1.0;
        double yy = 2 * (y + 0.5) / height - 1.0;
        double lng = pi * xx;
        double lat = 0.5 * pi * yy;

        double X, Y, Z;
        double D;
        int ix, iy;

        Z = cos(lat) * cos(lng);    // corresponds to original x
        Y = cos(lat) * sin(lng);    // corresponds to original y
        X = -sin(lat);              // corresponds to original z

        D = sqrt(X*X + Y*Y);        // distance in the XY plane
        lat = atan2(Z, D);
        lng = atan2(Y, X);

        ix = (0.5 * lng / pi + 0.5) * width - 0.5;
        iy = (lat / pi + 0.5) * height - 0.5;

        dest[y][(x + width / 4) % width] = src[iy][ix];
                                    // width/4 offset ist the 90° roll
                                    // % width wraps the longitude
    }
}

The quality of the resulting image is okay, but not as good as that of your reference image, especially near the poles. A better algorithm woul average and smoothze the colour values. The algorithm above just maps one destination pixel to a source pixel.

like image 172
M Oehm Avatar answered Oct 22 '22 10:10

M Oehm