您正在询问如何将360度鱼眼投影转换为等矩形投影。
为了做到这一点,对于鱼眼图像上的每个像素,您需要知道在输出图像上的位置。
您的输入图像是1920x1080,假设您想将其输出为相同大小的等矩形投影。
cx = 960; // center of circle on X-axis
cy = 540; // center of circle on Y-axis
radius = 540; // radius of circle
如果像素位于
(x,y)
在输入图像中,我们可以使用以下方法计算球面坐标:
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)
在等矩形图像中。为了不让实现成为可怕的“读者练习”,下面是一些示例Javascript代码,它使用了OP使用的Jimp库:
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');
});
});
另外,在你的情况下,你只有一半的球体(你看不到天空中的太阳)。所以你需要使用
var outputy = Math.round(phi_deg / 90.0 * outputheight)
. 为了保持正确的纵横比,您可能需要将高度更改为
540
还要注意,给定的实现可能根本没有效率,最好直接使用缓冲区。
不管怎么说,没有混合,我得出的结果如下所示:
因此,为了进行混合,可以使用最简单的方法,即最近邻方法。在这种情况下,您应该反转以上示例中的公式。不必将像素从输入图像移动到输出图像的正确位置,您可以遍历输出图像中的每个像素,并询问我们可以使用哪个输入像素。这将避免黑色像素,但仍可能显示伪影:
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');
});
});
以供参考,以便在笛卡尔坐标系和球面坐标系之间进行转换。这些是公式(
taken from here
z
在你的例子中只有1,一个所谓的“单位”球体,所以你可以把它排除在方程之外。你还应该明白,由于相机实际上是在三维拍摄照片,你也需要公式在三维工作。
下面是生成的输出图像:
由于我在您的问题中看不到您的原始输入图像,因此为了让任何人测试此答案中的代码,您可以使用以下图像:
运行代码时使用:
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)。如果你想得到一个好的结果,你必须同时对两个维度都这样做。但这真的应该是一个新的问题。