我想,因为它似乎是从一个气缸来的突起的方式翘曲的平面图像。
我有这样一个单位的形象:
我想它显示在2D图像是这样的:
我有点淘汰的几何预测。 我参观了像其他一些问题, 这可是我不明白我怎么会代表这些圆柱坐标(theta和RHO)为X,Y在笛卡尔(X,Y)平面坐标。 难道你们帮我一个煞费苦心的例子吗? 我编码它的iPhone和我没有使用任何第三方库像OpenCV的等
多谢。
我想,因为它似乎是从一个气缸来的突起的方式翘曲的平面图像。
我有这样一个单位的形象:
我想它显示在2D图像是这样的:
我有点淘汰的几何预测。 我参观了像其他一些问题, 这可是我不明白我怎么会代表这些圆柱坐标(theta和RHO)为X,Y在笛卡尔(X,Y)平面坐标。 难道你们帮我一个煞费苦心的例子吗? 我编码它的iPhone和我没有使用任何第三方库像OpenCV的等
多谢。
这是两个部分的答案,数学和代码
我喜欢这个问题,因为涉及的投影是有趣的,但数学仍然可以通过手不会有太大困难得到解决。 首先,要了解到底为什么图像扭曲它的方式是很重要的。 假设我们有一个单位的形象有凹圆筒坐在它前面。
第一步是做正投影所述图像移动到所述弯曲表面。
然后将这些点投影与背透视到图像平面上。 请注意,在这种情况下,整个图像缩小因为气缸的所有部分具有比图像平面更大的Z坐标。 在你的情况下,汽缸接触的左,右边缘,以便不缩水发生像面。 当点被投射返回注意到他们不再形成在图像平面上的扁平线,有气缸随x变化的,因为z的曲线坐标。
第一招是我们真正想要向后代表这个过程。 你可能首先想到的是要采取的每一个像素在原始图像,并将其移动到您的新形象。 如果您在您的新图像的每个像素在那里出现在旧形象,并设置其颜色实际上,它的效果要好得多。 这意味着,你需要做三两件事。
藏在心里的轨迹可以是一个有点棘手,所以我会尝试使用一致的术语。 首先,我假设你要保证你的缸边缘处的触动你的形象。 如果这是真的,那么你可以选择2个自由参数是圆柱半径和焦距。
圆在ZX平面的方程为
x^2+(z-z0)^2 = r^2
假定圆的中心位于z轴上。 如果气缸的边缘将触碰其具有宽度w和焦距f,则图像平面的边缘
omega^2+(f-z0)^2 = r^2 //define omega = width/2, it cleans it up a bit
z0 = f-sqrt(r^2-omega^2)
现在我们知道我们继续从相机在XIM在XC步骤2,项目线,通过图像平面到气缸缸体的所有参数。 这里是术语的快速图。
我们知道线我们预计在原点开始,并在跨越XIM像面。 我们可以写出其方程
x = xim*z/f
因为我们希望在它穿过缸结合方程x坐标
xim^2*z^2/f^2 + z^2 - 2*z*z0 +z0^2 - r^2 = 0
您可以使用二次公式计算z和再插回线方程得到X。 这两种方案对应于两块地的线接触了一圈,因为我们只在图像平面后出现这种情况的一个感兴趣,一个将永远有一个较大的x坐标,使用-b +的sqrt(...) 。 然后
xc = xim*z/f;
yc = yim*z/f;
除去正投影的最后一步是易刚落z分量和你做。
我知道你说你不使用OpenCV的,但我会用它,在我的示范为图像容器。 所有的操作都通过像素的基础上完成的,因此不应该是你很难将它转换为对任何图像容器使用的是工作。 首先我做了,它把来自图像最终图像中的坐标在原始图像中的坐标功能。 OpenCV的把它的图像原点在左上角这就是为什么我开始通过减去W / 2和h / 2,并最终通过加回
cv::Point2f convert_pt(cv::Point2f point,int w,int h)
{
//center the point at 0,0
cv::Point2f pc(point.x-w/2,point.y-h/2);
//these are your free parameters
float f = w;
float r = w;
float omega = w/2;
float z0 = f - sqrt(r*r-omega*omega);
float zc = (2*z0+sqrt(4*z0*z0-4*(pc.x*pc.x/(f*f)+1)*(z0*z0-r*r)))/(2* (pc.x*pc.x/(f*f)+1));
cv::Point2f final_point(pc.x*zc/f,pc.y*zc/f);
final_point.x += w/2;
final_point.y += h/2;
return final_point;
}
现在,所有剩下的就是在旧的图像样品中的新图像的每一个点。 有这样做的很多方面,我做的最简单的一个,我知道在这里,双线性插值。 另外这只是设置为在灰度工作,使其在色彩的工作是简单的只是过程适用于所有3个通道。 我只是认为这将是一个更清楚一点这种方式。
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
cv::Point2f current_pos(x,y);
current_pos = convert_pt(current_pos, width, height);
cv::Point2i top_left((int)current_pos.x,(int)current_pos.y); //top left because of integer rounding
//make sure the point is actually inside the original image
if(top_left.x < 0 ||
top_left.x > width-2 ||
top_left.y < 0 ||
top_left.y > height-2)
{
continue;
}
//bilinear interpolation
float dx = current_pos.x-top_left.x;
float dy = current_pos.y-top_left.y;
float weight_tl = (1.0 - dx) * (1.0 - dy);
float weight_tr = (dx) * (1.0 - dy);
float weight_bl = (1.0 - dx) * (dy);
float weight_br = (dx) * (dy);
uchar value = weight_tl * image.at<uchar>(top_left) +
weight_tr * image.at<uchar>(top_left.y,top_left.x+1) +
weight_bl * image.at<uchar>(top_left.y+1,top_left.x) +
weight_br * image.at<uchar>(top_left.y+1,top_left.x+1);
dest_im.at<uchar>(y,x) = value;
}
}
下面是对于f的样品输出= W / 2和r =瓦特