如何将两个remap()操作合并为一个?

时间:2015-04-29 13:07:35

标签: c++ opencv

我有一个紧凑的循环,在那里我得到一个摄影机图像,不失真并根据一些变换(例如透视变换)对其进行变换。我已经想过为每个操作使用<?php if(isset($_POST["Submit"]) && !empty($_POST["name"]) && !empty($_POST["phone"]) && !empty($_POST["course"])) { foreach($_POST as $key => $value) { if($key == "Submit") continue; echo $lhs[] = $key; //first array for left hand side echo $rhs[] = $value; //second array for right hand side } file_put_contents("file.txt", implode(PHP_EOL, array_map(function($v1, $v2){ return "$v1:$v2"; }, $lhs, $rhs)), FILE_APPEND); } ?> <form name="form1" method="post" action=""> Name: <input type="text" name="name"><br> Phone No: <input type="text" name="phone" /><br/> Course:<input type="text" name="course" /> <br /> <input type="submit" name="Submit" value="Sign Up"> </form> ,这已经比使用普通矩阵操作更有效了。

根据我的理解,应该可以将查找映射组合成一个,并在每次循环迭代中只调用一次重映射。有没有规范的方法来做到这一点?我不想自己实现所有的插值内容。

注意:该过程应适用于不同大小的地图。在我的特定情况下,不失真保留图像尺寸,而另一个转换将图像缩放到不同的尺寸。

插图代码:

cv::remap(...)

3 个答案:

答案 0 :(得分:5)

您可以在undistortMapX和undistortMapY上应用重映射。

cv::remap(undistortMapX, undistrtSkewX, skewMapX, skewMapY, cv::INTER_LINEAR);
cv::remap(undistortMapY, undistrtSkewY, skewMapX, skewMapY, cv::INTER_LINEAR);

比你可以使用:

cv::remap(originalImage , skewedImage, undistrtSkewX, undistrtSkewY, cv::INTER_LINEAR);

它的工作原理是因为skewMaps和undistortMaps是图像中的坐标数组,所以它应该类似于获取位置的位置......

修改(回复评论):

我想我需要做一些澄清。 remap()函数根据旧图像的像素计算新图像中的像素。在线性插值的情况下,新图像中的每个像素是距旧图像4个像素的加权平均值。根据提供的映射的值,权重在像素之间不同。如果该值大于或等于整数,则大部分权重取自单个像素。结果,新图像将是原始图像。另一方面,如果该值远不是整数(即整数+0.5),则权重是相似的。这将产生平滑效果。要了解我在说什么,请查看未失真的图像。您将看到图像的某些部分比其他部分更清晰/更平滑。

现在回到关于将两个重映射操作合并为一个时发生的情况的说明。组合图中的坐标是正确的,即skewedImage中的像素是从具有正确权重的originalImage的正确4个像素计算的。但它与两次重映射操作的结果不同。 undistortedImage中的每个像素是来自originalImage的4个像素的加权平均值。这意味着skewedImage的每个像素都是orginalImage的9-16像素的加权平均值。结论:使用单重映射() NOT 可能会给出与两次重映射()的使用相同的结果。

讨论两个可能的图像中的哪一个(单重映射()与双重映射())更好是非常复杂的。通常,尽可能少地进行插值是很好的,因为每个插值都会引入不同的伪像。特别是如果图像中的伪影不均匀(某些区域变得比其他区域更平滑)。在某些情况下,这些伪像可能对图像具有良好的视觉效果 - 比如减少一些抖动。但如果这是你想要的,你可以用更便宜和更一致的方式实现这一目标。例如,通过在重新映射之前平滑原始图像。

答案 1 :(得分:3)

在两个通用映射的情况下,除了使用@MichaelBurdinov建议的方法之外别无选择。

然而,在具有已知逆映射的两个映射的特殊情况下,另一种方法是手动计算映射。这种手动方法比双重重映射更精确,因为它不涉及坐标图的插值

在实践中,大多数有趣的应用程序都符合这种特殊情况。它也适用于您的情况,因为您的第一张地图对应于图像不失真(其逆操作是图像失真,与一个众所周知的分析模型相关联),而您的第二张图对应于透视变换(其反向可以通过分析表示)。

手动计算地图实际上非常简单。如文档(link)中所述,对于目标图像中的每个像素,这些地图包含(x,y)坐标,其中在源图像中找到适当的强度。以下代码段显示了如何在您的案例中手动计算地图:

int dst_width=...,dst_height=...;           // Initialize the size of the output image
cv::Mat Hinv=H.inv(), Kinv=K.inv();         // Precompute the inverse perspective matrix and the inverse camera matrix
cv::Mat map_undist_warped_x32f(dst_height,dst_width,CV_32F);    // Allocate the x map to the correct size (n.b. the data type used is float)
cv::Mat map_undist_warped_y32f(dst_height,dst_width,CV_32F);    // Allocate the y map to the correct size (n.b. the data type used is float)
// Loop on the rows of the output image
for(int y=0; y<dst_height; ++y) {
    std::vector<cv::Point3f> pts_undist_norm(dst_width);
    // For each pixel on the current row, first use the inverse perspective mapping, then multiply by the
    // inverse camera matrix (i.e. map from pixels to normalized coordinates to prepare use of projectPoints function)
    for(int x=0; x<dst_width; ++x) {
        cv::Mat_<float> pt(3,1); pt << x,y,1;
        pt = Kinv*Hinv*pt;
        pts_undist_norm[x].x = pt(0)/pt(2);
        pts_undist_norm[x].y = pt(1)/pt(2);
        pts_undist_norm[x].z = 1;
    }
    // For each pixel on the current row, compose with the inverse undistortion mapping (i.e. the distortion
    // mapping) using projectPoints function
    std::vector<cv::Point2f> pts_dist;
    cv::projectPoints(pts_undist_norm,cv::Mat::zeros(3,1,CV_32F),cv::Mat::zeros(3,1,CV_32F),intrinsic,distortion,pts_dist);
    // Store the result in the appropriate pixel of the output maps
    for(int x=0; x<dst_width; ++x) {
        map_undist_warped_x32f.at<float>(y,x) = pts_dist[x].x;
        map_undist_warped_y32f.at<float>(y,x) = pts_dist[x].y;
    }
}
// Finally, convert the float maps to signed-integer maps for best efficiency of the remap function
cv::Mat map_undist_warped_x16s,map_undist_warped_y16s;
cv::convertMaps(map_undist_warped_x32f,map_undist_warped_y32f,map_undist_warped_x16s,map_undist_warped_y16s,CV_16SC2);

注意:上面的H是您的透视变换,而K应该是与未失真图像相关联的相机矩阵,因此它应该是您的代码中的newCameraMatrix(BTW)不是initUndistortRectifyMap)的输出参数。根据您的具体数据,可能还有一些其他情况需要处理(例如,pt(2)可能为零时除以等等。)

答案 2 :(得分:0)

我遇到了同样的问题。我试图实现AldurDisciple的答案。而不是在循环中计算变换。我正在使用 mat.at&lt; Vec2f&gt;(x,y)= Vec2f(x,y)并将 perspectiveTransform 应用到此垫子。将第三个“1”通道添加到结果垫并应用 projectPoints 。 这是我的代码

Mat xy(2000, 2500, CV_32FC2);
float *pxy = (float*)xy.data;
for (int y = 0; y < 2000; y++)
    for (int x = 0; x < 2500; x++)
    {
        *pxy++ = x;
        *pxy++ = y;
    }

// perspective transformation of coordinates of destination image,
// which generates the map from destination image to norm points
Mat pts_undist_norm(2000, 2500, CV_32FC2);
Mat matPerspective =transRot3x3;
perspectiveTransform(xy, pts_undist_norm, matPerspective);

//add 3rd channel of 1
vector<Mat> channels;
split(pts_undist_norm, channels);
Mat channel3(2000, 2500, CV_32FC1, cv::Scalar(float(1.0)));
channels.push_back(channel3);
Mat pts_undist_norm_3D(2000, 2500, CV_32FC3);
merge(channels, pts_undist_norm_3D);

//projectPoints to extend the map from norm points back to the original captured image  
pts_undist_norm_3D = pts_undist_norm_3D.reshape(0, 5000000);
Mat pts_dist(5000000, 1, CV_32FC2);
projectPoints(pts_undist_norm_3D, Mat::zeros(3, 1, CV_64F), Mat::zeros(3, 1, CV_64F), intrinsic, distCoeffs, pts_dist);
Mat maps[2];
pts_dist = pts_dist.reshape(0, 2000);
split(pts_dist, maps);

// apply map
remap(originalImage, skewedImage, maps[0], maps[1], INTER_LINEAR);

用于映射到范数点的变换矩阵与AldurDisciple的答案中使用的变换矩阵略有不同。 transRot3x3 calibrateCamera 生成的 tvec rvec 组成。

double transData[] = { 0, 0, tvecs[0].at<double>(0), 0, 0, 
tvecs[0].at<double>(1), 0, 0,  tvecs[0].at<double>(2) };
Mat translate3x3(3, 3, CV_64F, transData);
Mat rotation3x3;
Rodrigues(rvecs[0], rotation3x3);

Mat transRot3x3(3, 3, CV_64F);
rotation3x3.col(0).copyTo(transRot3x3.col(0));
rotation3x3.col(1).copyTo(transRot3x3.col(1));
translate3x3.col(2).copyTo(transRot3x3.col(2));

<强>加了:

我意识到,如果唯一需要的地图是最终地图,为什么不将 projectPoints 用于 mat.at(x,y)= Vec2f(x,y,0)的垫子

//generate a 3-channel mat with each entry containing it's own coordinates
Mat xyz(2000, 2500, CV_32FC3);
float *pxyz = (float*)xyz.data;
for (int y = 0; y < 2000; y++)
    for (int x = 0; x < 2500; x++)
    {
        *pxyz++ = x;
        *pxyz++ = y;
        *pxyz++ = 0;
    }

// project coordinates of destination image,
// which generates the map from destination image to source image directly
xyz=xyz.reshape(0, 5000000);
Mat pts_dist(5000000, 1, CV_32FC2);
projectPoints(xyz, rvecs[0], tvecs[0], intrinsic, distCoeffs, pts_dist);
Mat maps[2];
pts_dist = pts_dist.reshape(0, 2000);
split(pts_dist, maps);

//apply map
remap(originalImage, skewedImage, maps[0], maps[1], INTER_LINEAR);
相关问题