#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
void CylindricalWarp()
{
cv::Mat imgMat = cv::imread("/img/1.jpg");
cv::namedWindow("Original", 0);
cv::imshow("Original", imgMat);
double r = imgMat.cols;
int w = atan2(imgMat.cols / 2, r) * 2 * r;
int h = imgMat.rows;
cv::Mat destImgMat = cv::Mat::zeros(cv::Size(w, h), CV_8UC3);
for (int y = 0; y < destImgMat.rows; y++)
{
for (int x = 0; x < destImgMat.cols; x++)
{
cv::Point2f current_pos(x, y);
float point_x = r * tan(current_pos.x / r - atan2(w, 2 * r)) + w / 2;
// float point_y = r * tan(current_pos.y / r - atan2(h, 2 * r)) + h / 2;
float point_y = (current_pos.y - h / 2) * sqrt(r * r + (w / 2 - x) * (w / 2 - x)) / r + h / 2;
cv::Point2f original_point(point_x, point_y);
cv::Point2i top_left((int)(original_point.x), (int)(original_point.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 > imgMat.cols - 2 || top_left.y < 0 || top_left.y > imgMat.rows - 2)
{
continue;
}
//bilinear interpolation
float dx = original_point.x - top_left.x;
float dy = original_point.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);
for (int k = 0; k < 3; k++)
{
uchar value = weight_tl * imgMat.at<cv::Vec3b>(top_left)[k] +
weight_tr * imgMat.at<cv::Vec3b>(top_left.y, top_left.x + 1)[k] +
weight_bl * imgMat.at<cv::Vec3b>(top_left.y + 1, top_left.x)[k] +
weight_br * imgMat.at<cv::Vec3b>(top_left.y + 1, top_left.x + 1)[k];
destImgMat.at<cv::Vec3b>(y, x)[k] = value;
}
}
}
cv::namedWindow("Cylindrical", 0);
cv::imshow("Cylindrical", destImgMat);
cv::waitKey(0);
cv::imwrite("img/cyl_lena.jpg", destImgMat);
}
void SphericalProjection()
{
cv::Mat imgMat = cv::imread("/img/1.jpg");
cv::namedWindow("Original", 0);
cv::imshow("Original", imgMat);
double r = imgMat.cols / 2;
int w = atan2(imgMat.cols / 2, r) * 2 * r;
int h = atan2(imgMat.rows / 2, r) * 2 * r;
// int w = imgMat.cols;
// int h = imgMat.rows;
// cout << cos(60 * CV_PI / 180) << endl;
cv::Mat destImgMat = cv::Mat::zeros(cv::Size(w, h), CV_8UC3);
for (int y = 0; y < destImgMat.rows; y++)
{
for (int x = 0; x < destImgMat.cols; x++)
{
cv::Point2f current_pos(x, y);
// double length = r / sqrt(abs(powf(cos((current_pos.x - w / 2) * CV_PI / (r * 180)), 2) - powf(cos((current_pos.y - h / 2) * CV_PI / (r * 180)), 2)));
double length = r * r / sqrt(r * r - (current_pos.x - w / 2) * (current_pos.x - w / 2) - (current_pos.y - h / 2) * (current_pos.y - h / 2));
float point_x = sin((current_pos.x - w / 2) / r) * length + imgMat.cols / 2;
float point_y = sin((current_pos.y - h / 2) / r) * length + imgMat.rows / 2;
cv::Point2f original_point(point_x, point_y);
cv::Point2i top_left((int)(original_point.x), (int)(original_point.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 > imgMat.cols - 2 || top_left.y < 0 || top_left.y > imgMat.rows - 2)
{
continue;
}
//bilinear interpolation
float dx = original_point.x - top_left.x;
float dy = original_point.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);
for (int k = 0; k < 3; k++)
{
uchar value = weight_tl * imgMat.at<cv::Vec3b>(top_left)[k] +
weight_tr * imgMat.at<cv::Vec3b>(top_left.y, top_left.x + 1)[k] +
weight_bl * imgMat.at<cv::Vec3b>(top_left.y + 1, top_left.x)[k] +
weight_br * imgMat.at<cv::Vec3b>(top_left.y + 1, top_left.x + 1)[k];
destImgMat.at<cv::Vec3b>(y, x)[k] = value;
}
}
}
cv::namedWindow("Spherical", 0);
cv::imshow("Spherical", destImgMat);
cv::waitKey(0);
cv::imwrite("img/spherical_projection.jpg", destImgMat);
}
int main()
{
CylindricalWarp();
SphericalProjection();
return 0;
}
效果图如下:
原图像
柱面投影变换
球面投影变换