Подтвердить что ты не робот

Изображение Warp появится в цилиндрической проекции

Я хочу деформировать плоское изображение таким образом, чтобы он представлял собой проекцию, исходящую из цилиндра.

У меня плоское изображение выглядит следующим образом:

I have this flat image

и я хочу показать это как-то вроде этого в 2D-изображении:

output image should be something like this

Я немного пошатнулся на геометрических проекциях. Я посетил некоторые другие вопросы, такие как , но я не понимаю, как я буду представлять эти цилиндрические координаты (тета и rho) в координаты x, y в декартовых (x, y ) самолет. Можете ли вы, ребята, помочь мне с примерным примером? Я кодирую его для iPhone, и я не использую стороннюю библиотеку, такую ​​как OpenCV и т.д.

Спасибо, куча.

4b9b3361

Ответ 1

Это ответ на 2 части, математика и код

математический

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

Demo Image 1

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

Demo Image 2

Затем эти точки проецируются с перспективой обратно на плоскость изображения. Обратите внимание, что в этом случае все изображение сжимается, потому что все части цилиндра имели большую координату z, чем плоскость изображения. В вашем случае цилиндр касается плоскости изображения на левом и правом краях, поэтому не происходит усадка. Когда точки проецируются назад, обратите внимание, что они больше не образуют плоскую линию на плоскости изображения, появляется кривая из-за координаты z цилиндра, изменяющейся в зависимости от x.

Demo Image 3

Первый трюк заключается в том, что мы действительно хотим представить этот процесс задом наперед. Сначала вы можете подумать, что хотите взять каждый пиксель исходного изображения и переместить его в новое изображение. Это на самом деле работает намного лучше, если вы проверите каждый пиксель в вашем новом изображении, где он появился на старом изображении, и установите его цвет. Это означает, что вам нужно сделать 3 вещи.

  1. Настройте параметры своего цилиндра
  2. Спроецируйте луч с камеры, проходя через каждую точку нового изображения, и найдите его координаты x, y, z на цилиндре.
  3. Используйте ортографическую проекцию, чтобы переместить этот луч обратно в плоскость изображения (просто означает падение компонента 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)

Теперь мы знаем все параметры цилиндра, который мы переходим к шагу 2, проецируем линии от камеры через плоскость изображения в xim к цилиндру в xc. Вот краткая схема терминологии.

Demo4

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

x = xim*z/f

Поскольку мы хотим, чтобы координата х, когда она проходит через цилиндр, объединить уравнения

xim^2*z^2/f^2 + z^2 - 2*z*z0 +z0^2 - r^2 = 0

Вы можете использовать квадратное уравнение, чтобы решить для z, а затем снова подключиться к уравнению линии, чтобы получить х. Эти два решения соответствуют двум местам, где линия касается окружности, поскольку нас интересует только то, что происходит после плоскости изображения, и это всегда будет иметь большую координату 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 = w.

enter image description here