3

Я хочу преобразовать обычные панорамные фотографии в полярные координаты, чтобы создать эффект «крошечной земли», но я не могу понять, как это решить. Я бы предположил, что есть полезный фильтр Core Graphics или сторонняя библиотека, но я не могу их найти.Применить полярные координаты к UIImage

Пример:

Tiny Earth Panorama

+0

Как примечание стороны, этот эффект может быть также получен непосредственно с помощью 360 [линзового камера] (https://en.wikipedia.org/wiki/Catadioptric_system# Photographic_catadioptric_lenses), как показано [здесь] (https://www.youtube.com/watch?v=nvOPIR8fa98) и используется для приложений в прохладном режиме. – AldurDisciple

+0

Спасибо, но цель состоит в том, чтобы использовать его для приложения, которое автоматически сшивает эту панораму. –

+0

Не могли бы вы привести пример ввода и соответствующий желаемый результат. –

ответ

4

Это на самом деле очень просто, вы просто должны применять полярные координаты. Вот полностью прокомментирован пример (реализовано в C++ и использование OpenCV только для структур данных и загрузки изображения и отображение):

#include <opencv2/highgui.hpp> 

// Function returning the bilinear interpolation of the input image at input coordinates 
cv::Vec3b interpolate(const cv::Mat &image, float x, float y) 
{ 
    // Compute bilinear interpolation weights 
    float floorx=std::floor(x), floory=std::floor(y); 
    float fracx=x-floorx, fracy=y-floory; 
    float w00=(1-fracy)*(1-fracx), w01=(1-fracy)*fracx, w10=fracy*(1-fracx), w11=fracy*fracx; 

    // Read the input image values at the 4 pixels surrounding the floating point (x,y) coordinates 
    cv::Vec3b val00 = image.at<cv::Vec3b>(floory, floorx); 
    cv::Vec3b val01 = (floorx<image.cols-1 ? image.at<cv::Vec3b>(floory, floorx+1) : image.at<cv::Vec3b>(floory, 0));  // Enable interpolation between the last right-most and left-most columns 
    cv::Vec3b val10 = image.at<cv::Vec3b>(floory+1, floorx); 
    cv::Vec3b val11 = (floorx<image.cols-1 ? image.at<cv::Vec3b>(floory+1, floorx+1) : image.at<cv::Vec3b>(floory+1, 0)); // Enable interpolation between the last right-most and left-most columns 

    // Compute the interpolated color 
    cv::Vec3b val_interp; 
    val_interp.val[0] = cv::saturate_cast<uchar>(val00.val[0]*w00+val01.val[0]*w01+val10.val[0]*w10+val11.val[0]*w11); 
    val_interp.val[1] = cv::saturate_cast<uchar>(val00.val[1]*w00+val01.val[1]*w01+val10.val[1]*w10+val11.val[1]*w11); 
    val_interp.val[2] = cv::saturate_cast<uchar>(val00.val[2]*w00+val01.val[2]*w01+val10.val[2]*w10+val11.val[2]*w11); 
    return val_interp; 
} 

// Main function 
void main() 
{ 
    const float pi = 3.1415926535897932384626433832795; 

    // Load and display color panorama image 
    cv::Mat panorama = cv::imread("../panorama_sd.jpg", cv::IMREAD_COLOR); 
    cv::namedWindow("Panorama"); 
    cv::imshow("Panorama", panorama); 

    // Infer the size of the final image from the dimensions of the panorama 
    cv::Size result_size(panorama.rows*2, panorama.rows*2); 
    float ctrx=result_size.width/2, ctry=result_size.height/2; 

    // Initialize an image with black background, with inferred dimensions and same color format as input panorama 
    cv::Mat tiny_earth_img = cv::Mat::zeros(result_size, panorama.type()); 
    cv::Vec3b *pbuffer_img = tiny_earth_img.ptr<cv::Vec3b>(); // Get a pointer to the buffer of the image (sequence of 8-bit interleaved BGR values) 

    // Generate the TinyEarth image by looping over all its pixels 
    for(int y=0; y<result_size.height; ++y) { 
     for(int x=0; x<result_size.width; ++x, ++pbuffer_img) { 

      // Compute the polar coordinates associated with the current (x,y) point in the final image 
      float dx=x-ctrx, dy=y-ctry; 
      float radius = std::sqrt(dx*dx+dy*dy); 
      float angle = std::atan2(dy,dx)/(2*pi); // Result in [-0.5, 0.5] 
      angle = (angle<0 ? angle+1 : angle); // Result in [0,1[ 

      // Map the polar coordinates to cartesian coordinates in the panorama image 
      float panx = panorama.cols*angle; 
      float pany = panorama.rows-1-radius; // We want the bottom of the panorama to be at the center 

      // Ignore pixels which cannot be linearly interpolated in the panorama image 
      if(std::floor(panx)<0 || std::floor(panx)+1>panorama.cols || std::floor(pany)<0 || std::floor(pany)+1>panorama.rows-1) 
       continue; 

      // Interpolate the panorama image at coordinates (panx, pany), and store this value in the final image 
      pbuffer_img[0] = interpolate(panorama, panx, pany); 
     } 
    } 

    // Display the final image 
    cv::imwrite("../tinyearth.jpg", tiny_earth_img); 
    cv::namedWindow("TinyEarth"); 
    cv::imshow("TinyEarth", tiny_earth_img); 
    cv::waitKey(); 
} 

панорама ввода образца (source):

enter image description here

Результирующего изображение:

enter image description here

EDIT:

Чтобы ответить на ваше замечание о черных границах, вы можете настроить функцию сопоставления (которая отображает координаты пикселей в конечном изображении на пиксельные координаты в панорамном изображении), чтобы достичь того, что вы хотите сделать. Вот некоторые примеры: панорама

Источник:

enter image description here

1) Оригинальное отображение: пикселей с радиусом> panorama.rows/2 остается не-прикоснулся (следовательно, вы можете иметь все, что фоновое изображение показать есть)

float panx = panorama.cols*angle; 
float pany = panorama.rows-1-radius; 

Результат:

enter image description here

2) Отображение самой близкой точки: пиксели с радиусом> panorama.rows/2 отображаются на ближайший действительный пиксель в панораме.

float panx = panorama.cols*angle; 
float pany = std::max(0.f,panorama.rows-1-radius); 

Результат:

enter image description here

3) Увеличенный в отображении: крошечное-земля изображение увеличено так, что пиксели с радиусом> panorama.rows/2 преобразуются в действительные панорамы пикселей однако некоторые части панорамы теперь отображается вне крошечных околоземного изображения (вверху/внизу/влево/вправо)

float panx = panorama.cols*angle; 
float pany = panorama.rows-1-0.70710678118654752440084436210485*radius; 

Результат:

enter image description here

4) логарифмическая отображение: нелинейное отображение с участием функции журнала используется, чтобы минимизировать участки панорамы, которые отображаются вне крошечных околоземных изображений (вы можете настроить константу 100 в масштабе более или менее).

const float scale_cst = 100; 
float panx = panorama.cols*angle; 
float pany = (panorama.rows-1)*(1-std::log(1+scale_cst*0.70710678118654752440084436210485*radius/panorama.rows)/std::log(1+scale_cst)); 

Результат:

enter image description here

+0

Похоже, что он должен работать, благодаря кучу. –

+0

Любая идея, как избавиться от черных границ? Любая интерполяция лучше черного. –

+0

Конечно, вы можете делать много вещей: все сводится к определению точного сопоставления, которое вы хотите. Я отредактирую свой ответ несколькими примерами. – AldurDisciple

 Смежные вопросы

  • Нет связанных вопросов^_^