在节点js中将360度视图转换为等角矩形? [英] Converting 360 degree view to equirectangular in node js?

查看:100
本文介绍了在节点js中将360度视图转换为等角矩形?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去的两天里,我一直在尝试将360度摄像机(单鱼眼图像)转换为node js中的等矩形查看器.在stackoverflow中,用伪代码询问和回答相同的问题.我一直在尝试将伪代码转换为节点js,并清除了一些错误.现在,项目运行没有错误,但输出图像为空白.

I have been trying to convert the 360 degree camera, single fish eye image, to equirectangular viewer in node js for the past two days. In stackoverflow, the same question is asked and answered in pseudo code. I have been trying to convert pseudo code to node js and cleared some errors. Now the project runs without error but the output image is blank.

根据该伪指令,我不知道 polar_w,polar_h和geo_w,geo_h,geo和polar 的值,因此,它给出了静态值以显示输出.这是我用来将伪代码转换为节点js的链接. 如何将球面坐标转换为等角投影坐标?

From that pseudo, I dont know the polar_w, polar_h and geo_w, geo_h, geo and polar value, so, it gave static value to show the output. Here is a link which i followed to convert pseudo code to node js. How to convert spherical coordinates to equirectangular projection coordinates?.

这是我尝试将球形图像转换为等角形查看器的代码:

Here is the code I tried for converting spherical image to equirectangular viewer:

 exports.sphereImage=(request, response)=>{


 var Jimp = require('jimp');


  // Photo resolution

var img_w_px = 1280;
var img_h_px = 720;

var polar_w = 1280;
var polar_h = 720;
var geo_w = 1280;
var geo_h = 720;

var img_h_deg = 70;
var img_w_deg = 30;

  // Camera field-of-view angles

var img_ha_deg = 70;
var img_va_deg = 40;


  // Camera rotation angles

 var  hcam_deg = 230;
 var  vcam_deg = 60;

  // Camera rotation angles in radians

 var hcam_rad = hcam_deg/180.0*Math.PI;
 var vcam_rad = vcam_rad/180.0*Math.PI;

 // Rotation around y-axis for vertical rotation of camera

  var rot_y = [

   [Math.cos(vcam_rad), 0, Math.sin(vcam_rad)],
   [0, 1, 0],
  [-Math.sin(vcam_rad), 0, Math.cos(vcam_rad)]

    ];

 // Rotation around z-axis for horizontal rotation of camera

  var rot_z = [

 [Math.cos(hcam_rad), -Math.sin(hcam_rad), 0],
 [Math.sin(hcam_rad), Math.cos(hcam_rad), 0],
 [0, 0, 1]

 ];


  Jimp.read('./public/images/4-18-2-42.jpg', (err, lenna) => {


    polar = new Jimp(img_w_px, img_h_px);
    geo = new Jimp(img_w_px, img_h_px);

  for(var i=0; i<img_h_px; ++i)
  {
  for(var j=0; j<img_w_px; ++j)
  {
    // var p = img.getPixelAt(i, j);

    var p = lenna.getPixelColor(i, j)
    // var p = getPixels(img, { x: i, y: j })

    // Calculate relative position to center in degrees
    var p_theta = (j - img_w_px / 2.0) / img_w_px * img_w_deg / 180.0 * Math.PI;
    var p_phi = -(i - img_h_px / 2.0) / img_h_px * img_h_deg / 180.0 *Math. PI;

    // Transform into cartesian coordinates
    var p_x = Math.cos(p_phi) * Math.cos(p_theta);
    var p_y = Math.cos(p_phi) * Math.sin(p_theta);
    var p_z = Math.sin(p_phi);
    var p0 = {p_x, p_y, p_z};

    // Apply rotation matrices (note, z-axis is the vertical one)
    // First vertically
    var p1 = rot_y[1][2][3] * p0;
    var p2 = rot_z[1][2][3] * p1;

    // Transform back into spherical coordinates
    var theta = Math.atan2(p2[1], p2[0]);
    var phi = Math.asin(p2[2]);

    // Retrieve longitude,latitude
    var longitude = theta / Math.PI * 180.0;
    var latitude = phi / Math.PI * 180.0;

    // Now we can use longitude,latitude coordinates in many different 
    projections, such as:
    // Polar projection
    {
        var polar_x_px = (0.5*Math.PI + phi)*0.5 * Math.cos(theta) 
   /Math.PI*180.0 * polar_w;
        var polar_y_px = (0.5*Math.PI + phi)*0.5 * Math.sin(theta) 
    /Math.PI*180.0 * polar_h;
        polar.setPixelColor(p, polar_x_px, polar_y_px);
    }
    // Geographical (=equirectangular) projection
    {
        var geo_x_px = (longitude + 180) * geo_w;
        var geo_y_px = (latitude + 90) * geo_h;
        // geo.setPixel(geo_x_px, geo_y_px, p.getRGB());
        geo.setPixelColor(p, geo_x_px, geo_y_px);
    }
      // ...
 }
}

  geo.write('./public/images/4-18-2-42-00001.jpg');
 polar.write('./public/images/4-18-2-42-00002.jpg');



 });


}

并尝试了另一种方法,将图像分成四个部分以检测汽车.使用image-slice模块和读写jimp模块将图像切成四个部分.但不幸的是,汽车检测不正确.

And tried another method by slicing image into four parts to detect car. Sliced image into four parts using image-slice module and to read and write jimp module is used. But unfortunately cars not detected properly.

这是我用于切片图像的代码:

Here is the code i used for slicing image:

 exports.sliceImage=(request, response)=>{

var imageToSlices = require('image-to-slices');
var lineXArray = [540, 540];
var lineYArray = [960, 960];
var source = './public/images/4-18-2-42.jpg'; // width: 300, height: 300

imageToSlices(source, lineXArray, lineYArray, {
    saveToDir: './public/images/',
    clipperOptions: {
        canvas: require('canvas')
    }    
}, function() {
    console.log('the source image has been sliced into 9 sections!');
});


 }//sliceImage 

为了从图像中检测汽车,我使用了opencv4nodejs.未正确检测到汽车.这是我用来检测汽车的代码:

And for detect car from image i used opencv4nodejs. Cars are not detected properly. here is the code i used for detect car:

   function runDetectCarExample(img=null){
        if(img==null){

         img = cv.imread('./public/images/section-1.jpg');
    }else
    {
         img=cv.imread(img);
    }
        const minConfidence = 0.06;

        const predictions = classifyImg(img).filter(res => res.confidence > minConfidence && res.className=='car');

        const drawClassDetections = makeDrawClassDetections(predictions);

        const getRandomColor = () => new cv.Vec(Math.random() * 255, Math.random() * 255, 255);

        drawClassDetections(img, 'car', getRandomColor);
        cv.imwrite('./public/images/section-'+Math.random()+'.jpg', img);
        var name="distanceFromCamera";
        var focalLen= 1.6 ;//Focal length in mm
        var realObjHeight=254 ;//Real Height of Object in mm
        var cameraFrameHeight=960;//Height of Image in pxl
        var imgHeight=960;//Image Height in pxl
        var sensorHeight=10;//Sensor height in mm
        var R = 6378.1 //#Radius of the Earth
        var brng = 1.57 //#Bearing is 90 degrees converted to radians.
        var hc=(200/100);//Camera height in m
        predictions
            .forEach((data)=> {

                // imgHeight=img.rows;//Image Height in pxl
                // realObjHeight=data.rect.height;
                // data.rect[name]=((focalLen)*(realObjHeight)* 
         (cameraFrameHeight))/((imgHeight)*(sensorHeight));

                var dc=(((data.rect.width * focalLen) / img.cols)*2.54)*100; // meters
                console.log(Math.floor(parseInt(data.rect.width)));
                // var dc=((Math.floor(parseInt(data.rect.width)* 0.264583) * focalLen) / img.cols); // mm


                var lat1=13.0002855;//13.000356;
                var lon1=80.2046441;//80.204632;
                // Gate 13.0002855,80.2046441
                // Brazil Polsec : -19.860566, -43.969436
                // var d=Math.sqrt((dc*dc)+(hc*hc));
                // d=(data.rect[name])/1000;
                data.rect[name]=d=dc/1000;
                lat1 =toRadians(lat1);
                lon1 = toRadians(lon1);
                brng =toRadians(90);
                // lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
                //      Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));

                // lon2 = lon1 + 
             Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
                //              Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));


        var lat2 = Math.asin(Math.sin(lat1) * Math.cos(d/6371) +
                      Math.cos(lat1) * Math.sin(d/6371) * Math.cos(brng));

        var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(d/6371) * Math.cos(lat1),
                              Math.cos(d/6371) - Math.sin(lat1) * Math.sin(lat2));

                lat2 = toDegrees(lat2);
                lon2 = toDegrees(lon2);
                data.rect['latLong']=lat2+','+lon2;
                // console.log(brng);

            });




        response.send(predictions);
        cv.imshowWait('img', img);
    };

这是鱼眼图像,需要将其转换为等角矩形.

here is the fish eye image which need to be converted to equirectangular.

任何帮助都值得赞赏的人....

Any help much appreciated pls....

推荐答案

您在问如何将360度鱼眼投影转换为等角投影.

You are asking how to convert a 360deg fish-eye projection to an equirectangular projection.

为此,对于鱼眼图像上的每个像素,您需要知道将其放置在输出图像上的位置.

In order to do this, for every pixel on the fish-eye image you need to know where to place in onto the output image.

您输入的图像为1920x1080,让我们假设您要将其输出到相同大小的等角投影.

Your input image is 1920x1080, let us assume you want to output it to an equirectangular projection of the same size.

输入圆映射定义为:

cx = 960; // center of circle on X-axis
cy = 540; // center of circle on Y-axis
radius = 540; // radius of circle

如果输入图像中(x,y)处有一个像素,那么我们可以使用以下方法计算球面坐标:

If you have a pixel at (x,y) in the input image, then we can calculate the spherical coordinates using:

dx = (x - cx) * 1.0 / radius;
dy = (y - cy) * 1.0 / radius;
theta_deg = atan2(dy, dx) / MATH_PI * 180;
phi_deg = acos(sqrt(dx*dx + dy*dy)) / MATH_PI * 180;
outputx = (theta_deg + 180) / 360.0 * outputwidth_px;
outputy = (phi_deg + 90) / 180.0 * outputheight_px;

因此,我们将鱼眼图像中的(x,y)转换为等角矩形图像中的(outputx,outputy).为了不使该实现成为令人恐惧的读者阅读运动",下面是一些使用OP使用的Jimp库的示例Javascript代码:

So there we translated (x,y) from the fish-eye image to the (outputx,outputy) in the equirectangular image. In order to not leave the implementation as the dreaded "exercise to the reader", here is some sample Javascript-code using the Jimp-library as used by the OP:

var jimp = require('jimp');
var inputfile = 'input.png';
jimp.read(inputfile, function(err, inputimage)
{
    var cx = 960;
    var cy = 540;
    var radius = 540;
    var inputwidth = 1920;
    var inputheight = 1080;
    var outputwidth = 1920;
    var outputheight = 1080;
    new jimp(outputwidth, outputheight, 0x000000ff, function(err, outputimage)
    {
        for(var y=0;y<inputheight;++y)
        {
            for(var x=0;x<inputwidth;++x)
            {
                var color = inputimage.getPixelColor(x, y);
                var dx = (x - cx) * 1.0 / radius;
                var dy = (y - cy) * 1.0 / radius;
                var theta_deg = Math.atan2(dy, dx) / Math.PI * 180;
                var phi_deg = Math.acos(Math.sqrt(dx*dx + dy*dy)) / Math.PI * 180;
                var outputx = Math.round((theta_deg + 180) / 360.0 * outputwidth);
                var outputy = Math.round((phi_deg + 90) / 180.0 * outputheight);
                outputimage.setPixelColor(color, outputx, outputy);
            }
        }
        outputimage.write('output.png');
    });
});

请注意,您仍然需要将像素与相邻像素混合(由于与调整图像大小时相同的原因).

Note that you will still need to do blending of the pixel with neighbouring pixels (for the same reason as when you're resizing the image).

此外,在您的情况下,您只有球的一半(看不到天空中的太阳).因此,您将需要使用var outputy = Math.round(phi_deg / 90.0 * outputheight).为了保持正确的宽高比,您可能需要将高度更改为540.

Additionally, in your case, you only have half of the sphere (you can't see the sun in the sky). So you would need to use var outputy = Math.round(phi_deg / 90.0 * outputheight). In order to keep the right aspect ratio, you might want to change the height to 540.

还要注意,给定的实现可能根本没有效率,最好直接使用缓冲区.

Also note that the given implementation may not be efficient at all, it's better to use the buffer directly.

无论如何,在没有混合的情况下,我得出了如下所示的结果:

Anyway, without blending I came up with the result as demonstrated here:

因此,为了进行混合,您可以使用最简单的方法,即最近的邻居方法.在这种情况下,您应该在上面的示例中反转公式.无需将像素从输入图像移动到输出图像中的正确位置,而是可以遍历输出图像中的每个像素,然后询问我们可以使用哪个输入像素.这样可以避免出现黑色像素,但仍可能显示出伪像:

So in order to do blending, you could use the simplest method which is the nearest neighbour approach. In that case, you should invert the formulas in the above example. Instead of moving the pixels from the input image to the right place in the output image, you can go through every pixel in the output image and ask which input pixel we can use for that. This will avoid the black pixels, but may still show artifacts:

var jimp = require('jimp');
var inputfile = 'input.png';
jimp.read(inputfile, function(err, inputimage)
{
    var cx = 960;
    var cy = 540;
    var radius = 540;
    var inputwidth = 1920;
    var inputheight = 1080;
    var outputwidth = 1920;
    var outputheight = 1080/2;
    var blendmap = {};
    new jimp(outputwidth, outputheight, 0x000000ff, function(err, outputimage)
    {
        for(var y=0;y<outputheight;++y)
        {
            for(var x=0;x<outputwidth;++x)
            {
                var theta_deg = 360 - x * 360.0 / outputwidth - 180;
                var phi_deg = 90 - y * 90.0 / outputheight;
                var r = Math.sin(phi_deg * Math.PI / 180)
                var dx = Math.cos(theta_deg * Math.PI / 180) * r;
                var dy = Math.sin(theta_deg * Math.PI / 180) * r;
                var inputx = Math.round(dx * radius + cx);
                var inputy = Math.round(dy * radius + cy);
                outputimage.setPixelColor(inputimage.getPixelColor(inputx, inputy), x, y);
            }
        }
        outputimage.write('output.png');
    });
});

供参考,以便在直角坐标系和球形坐标系之间进行转换.这些是公式(从此处获取).请注意,在您的情况下,z仅为1,即所谓的单位"球体,因此您可以将其排除在方程式之外.您还应该了解,由于相机实际上是在三维上拍摄照片,因此还需要在三维上使用公式.

For reference, in order to convert between Cartesian and Spherical coordinate systems. These are the formulas (taken from here). Note that the z is in your case just 1, a so-called "unit" sphere, so you can just leave it out of the equations. You should also understand that since the camera is actually taking a picture in three dimensions, you also need formulas to work in three dimensions.

这是生成的输出图像:

由于我不再在问题中看到您的原始输入图片,因此为了让任何人测试此答案中的代码,您可以使用以下图片:

Since I don't see your original input image in your question anymore, in order for anyone to test the code from this answer, you can use the following image:

使用以下代码运行代码:

Run the code with:

mkdir /tmp/test
cd /tmp/test
npm install --permanent jimp
cat <<EOF >/tmp/test/main.js
... paste the javascript code from above ...
EOF
curl https://i.stack.imgur.com/0zWt6.png > input.png
node main.js


注意:为了进一步改善混合效果,应删除Math.round.因此,例如,如果您需要抓取x处的像素为0.75,并且x = 0处左侧的像素为白色,而x = 1右侧的像素则为黑色.然后,您想将两种颜色混合为深灰色(使用0.75的比率).如果要获得良好的效果,则必须同时对两个尺寸执行此操作.但这真的应该是一个新问题,恕我直言.


Note: In order to further improve the blending, you should remove the Math.round. So for instance, if you need to grab a pixel at x is 0.75, and the pixel on the left at x = 0 is white, and the pixel on the right at x = 1 is black. Then you want to mix both colors into a dark grey color (using ratio 0.75). You would have to do this for both dimensions simultaneously, if you want a nice result. But this should really be in a new question imho.

这篇关于在节点js中将360度视图转换为等角矩形?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆