Перейти к содержанию
    

Точно описать и исправлять дисторсию китайского широкоугольного объектива

6 часов назад, iiv сказал:

не такой, как у меня - и в центре, и по краям свои коэффициенты

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

 

6 часов назад, iiv сказал:

кажется, понял о чем Вы имели ввиду ... и передать это все по компорту наверх на какой-то обычный компьютер

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 hours ago, _pv said:

то что на картинке из-за своей несимметричности похоже надо лечить каким-то произвольным полиномом от x,y, а не радиально симетричным по r с чётными степенями.

качество картинки отвратительное конечно, но если пройтись градиентным фильтром и размазать

gr = ImageAdjust[GaussianFilter[GradientFilter[GaussianFilter[ColorConvert[img, "Grayscale"], 2], 2], 4]]

что-то разглядеть можно

image.thumb.png.8f3df366c3892b2b17c679ba7934b522.png

вытащить данные и натянуть 2х мерный фит экспоненты, погнутой вдоль полинома

grd = ImageData[gr];
data = Flatten[Table[{x, y, grd[[y, x]]}, {y, Length[grd]}, {x, Length[grd[[1]]]}], 1];

expr = a*Exp[-(y - (y0 + y1*x + y2*x^2 + y3*x^3 + y4*x^4))^2/2/sy] + b;
fit = FindFit[data, expr, {{y0, #}, {y1, 0}, {y2, 0}, {y3, 0}, {y4, 0}, {sy, 5}, {a, 0.2}, {b, 0.3}}, {x, y}] & /@ {75, 130, 175, 220, 264, 308, 351, 393, 435, 490}

выглядит как-то так вблизи

image.thumb.png.e459d317f80f0e64bb2a335b489d4472.png

 

{{y0 -> 87.7292, y1 -> -0.146309, y2 -> 0.000279265, y3 -> 3.62452*10^-7, y4 -> -7.28475*10^-10, sy -> 4.45341, a -> 0.11059, b -> 0.293883},
{y0 -> 134.557, y1 -> -0.287593, y2 -> 0.00148697, y3 -> -2.73506*10^-6, y4 -> 1.73526*10^-9, sy -> 5.88032, a -> 0.186041, b -> 0.292935},
{y0 -> 175.161, y1 -> -0.216324, y2 -> 0.00129499, y3 -> -2.53724*10^-6, y4 -> 1.6346*10^-9, sy -> 6.31835, a -> 0.207331, b -> 0.29263},
{y0 -> 219.418, y1 -> -0.146834, y2 -> 0.000903935, y3 -> -1.76892*10^-6, y4 -> 1.11553*10^-9, sy -> 5.16709, a -> 0.246005, b -> 0.292465},
{y0 -> 263.825, y1 -> -0.0721467, y2 -> 0.000454634, y3 -> -9.13689*10^-7, y4 -> 5.80904*10^-10, sy -> 4.6724, a -> 0.271664, b -> 0.292343},
{y0 -> 307.561, y1 -> 0.0313504, y2 -> -0.000230062, y3 -> 4.92199*10^-7, y4 -> -3.41197*10^-10, sy -> 4.70917, a -> 0.265807, b -> 0.292388}, 
{y0 -> 351.353, y1 -> 0.111996, y2 -> -0.000739691, y3 -> 1.48542*10^-6, y4 -> -9.68673*10^-10, sy -> 5.46529, a -> 0.236075, b -> 0.292496},
{y0 -> 393.196, y1 -> 0.174529, y2 -> -0.000967397, y3 -> 1.69454*10^-6, y4 -> -9.70441*10^-10, sy -> 6.22441, a -> 0.193682, b -> 0.292795},
{y0 -> 435.257, y1 -> 0.187255, y2 -> -0.000874856, y3 -> 1.35374*10^-6, y4 -> -7.22933*10^-10, sy -> 6.19187, a -> 0.199524, b -> 0.292738},
{y0 -> 475.104, y1 -> 0.158682, y2 -> -0.000352023, y3 -> -2.07054*10^-7, y4 -> 5.87065*10^-10, sy -> 5.55553, a -> 0.140875, b -> 0.293453}}

сам по себе, особенно на краях, фит немного не справился,

Show[ImageReflect[gr], Plot[((y0 + y1*x + y2*x^2 + y3*x^3 + y4*x^4) /. #) & /@ {fit}, {x, 1, Length[grd[[1]]]}, PlotStyle -> {Red, Thick}]]

image.png.5dbe5ce22c859b7acac1b029ec69339b.png

надо ему аккуратнее подсказывать начальные условия, ну или картинку с нормальной освещённостью снять.

останется сделать то же самое по вертикали, получится dx,dy от (x,y) и потом вывернуть полиномы для обратного преобразования.

но вот делать это полностью автоматически без подсказок внутри esp32 я бы не стал, на время калибровки wifi/синезуб поднять для получения картинки будет имхо куда проще.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 9/8/2022 at 9:38 AM, iiv said:

хочется из говна и палок, ибо бюджет не позволяет.

можно на RPi 3 поднять, эта модель уже менее популярна и стоит наверное от силы 30-40 евро, не сильно дороже вашего модуля. Зато можно сделать, как @_pv показал, с OpenCV или библиотекой на ваш выбор.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

28 minutes ago, alexunder said:

можно на RPi 3 поднять,

то, что я описал и в esp32 упихать можно попробовать.

но если всё равно придётся вытаскивать картинку наружу, то проще на телефон "калибровку" переложить, тем более что если это действителньо линзы сильно кривые, возможно сделать полностью автоматическую калибровку, с единственной кнопкой "сделать за@#$%ь", которая работает железно в любых условиях будет сложнее чем "полуавтоматическую", где параметры натягиваемой сетки можно немного на ходу подкрутить руками, глядя на кривой результат, то есть добавляется ещё какой-то пользовательский интерфейс.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо огромное, plain, _pv, alexunder за очень полезные советы и помощь!!!

 

Во-первых, попробовал собрать простенький стенд, где бы можно было бы максимально точно зафиксировать камеру перпендикулярно миллиметровки без ИК фильтров и повторил все измерения.

 

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

 

2 plain - спасибо, понял, что Вы имели ввиду, да, полностью согласен с Вами, на стенде измерить будет на порядки проще, а результат измерения, сгладив сразу можно в виде таблицы сохранить в esp32.

 

2 _pv - спасибо! Я как-то зациклился сразу дисторсию считать, а Вы как раз хорошо надоумили как это сделать правильно, чтобы оценить ее величину.

 

2 alexunder - спасибо, за идею про PRI3. Я думал над этим, и даже что-то пробовал. У меня там возникает только одна сложность - я не смог с камеры в реальном времени вытаскивать сам битстрим, то есть я что-то таки нашел, но оно по качеству было примерно как у esp32, то есть около 8 бит на 20Мсемплов, а когда хочется вытаскивать больше, то все ехало очень не стабильно. Удобство esp32 в моем случае в том, что я могу raw данные сразу с камеры брать, пусть в не очень супер сильном разрешении. Если бы не было дисторсии, я обработку того, что мне надо на скорости 2МПиксельные картинки по 10 фреймов в секунду на одном ядре esp32 научился обрабатывать, чтобы оставить второе ядро на прием данных и подправление дисторсии. Надо еще раз в RPI3 поиграться, возможно там удастся что-то вытащить лучше.

c9-1.jpg

c8-1.jpg

c7-1.jpg

c6-1.jpg

c5-1.jpg

c4-1.jpg

c3-1.jpg

c2-1.jpg

c1-1.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

картинки стали симметричными, можно и двумя-тремя радиальными коэффициентами обойтись c 1+Ki*r^2i как обычно делают.

ещё для количественной оценки кривизны наверное можно просто преобразование Хафа от картинки делать и смотреть на распределение "углов" линий, после правильной коррекции там должна быть только пара узких вертикальных полос соответсвующих строго вертикальным ( \( \theta = \pi / 2 \) ) и горизонтальным линиям( \( \theta = 0 \) ). их ширина и будет показывать общую "кривость" объектива, которую и надо минимизировать подбирая коэффициенты перед r^2i.

но картинку почистить надо, или хотя бы миллимитровые линии убрать, которые только дополнительный шум дают.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 лет назад работал в конторе, которая именно эти проблемы решала. Там очень любили книгу:
Charles Poynton
Video and HDTV Algorithms and Interfaces.

У меня она есть в pdf.
Здесь можно как-то переслать?

Просмотрел ее. Там о другом речь идет.

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 hours ago, Tarbal said:

15 лет назад работал в конторе, которая именно эти проблемы решала. Там очень любили книгу:
Charles Poynton
Video and HDTV Algorithms and Interfaces.

У меня она есть в pdf.
Здесь можно как-то переслать?

 

 

Всё равно выложите пож-ста : )
Если она не больше 10MB то можно прикрепить к сообщению
А лучше на F Т Р

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо большое за интересную книжку, на зет-либе она есть, но его последнюю неделю лихорадит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

  

On 9/8/2022 at 2:38 PM, iiv said:

Хочу научиться калибровать такие камеры, чтобы полностью удалить дисторсию.

Дисторсия от "Distortion", искажение. Distorted - искаженный.

1. Возможно идеальное решение методом цифровой бработки сигнала и цифровой 2D фильтрации. С изучением исходной системы единичными функциями Дирака, сверткой, и получением полного отклика. Подготовкой требуемой матрицы 2D фильтра, и далее в каждом кадре покадровой быстрой сверткой. Но для микроконтроллеров это вычислительно слишком сложно. Поэтому обсуждать это далее бессмысленно.

2. Brown–Conrady, а лучше Division Model.  Идём сюда  https://en.wikipedia.org/wiki/Distortion_(optics)#Software_correction
Далее смотрим на формулу в статье Wikipedia, сразу обращая внимание что в большинссве случаев мы имеем дело с радиальной дисторсией и "division model" (вторая ф-ла)  более точна, её и советую использовать.

Я это писал ~30лет назад и это работало, только оно не исправляло, а вносило искажения.

Общая схема алгоритма такова:
Используются 2шт. Двумерных буфера (двумерных [y][x] массива). В первом, назовем его Src[y][x], лежит исходная (искаженная) картинка.  Во втором, назовем его Target[y][x] строится неискаженная (целевая) картинка.  Попиксельно, в двух. вложенных циклах для каждой x,y координаты пиксели переносятся из Src в Target.  Координаты пикселя Target[y][x] известны, т.к. алгоритм "бежит" по ним, тогда как Src-координаты, откуда для каждого Target-пикселя взять из Src - вычисляются. Это известно как reverse-transform.

Как это указано формула для direct transform, она отыскивает выходные Target-координаты Xu, Yu  для известных Xd, Yd  Src-координат входного искаженного изображения. Такое пойдет только для первичного теста, так как если отыскивать координаты Target пикселей в итоге останутся дыры и дубликаты.
Итоговое решение в том что мы вычисляем и программно записываем пары [Xu, Yu]  и  [Xd, Yd], с некоторым оверсэмплингом чтобы избежать дыр и потом чисто программно произведим реверс, чтобы для одной пары Target-координат отыскивалась одна пара Src-координат попутно программно устраняя дубликаты.
Замечания по реализации.
а) Xc,Yc можно принять в центр, т.е. 0,0 и отбросить.
б) Координаты со знаком. Т.е. центру картинки соответствует [Xd=0, Yd=0].
Euclidian Distance r есть радиус, т.е. просто расстояние для текущего пикселя от центра картинки.  
в)  Если есть желание зачем-то использовать не Division Model, а Brown-Conrady, то Коэффиценты Pn и члены соответствующие тангенциальной дисторсии можно попробовать отбросить, можно также попробовать отбросить все коэффициенты K2 и старше K2  (т.е. отбросить четвертые и более высокие степени), оставив для начала только вторые степени, т.е. r^2, это будет работать, тем более что для Division Model:  Using this model, a single term is usually sufficient to model most cameras[9]
г) Вычисленные кооординаты потребуется клиппировать.

Примерный упрощенный псведо-код выполняющий direct transform коррекции дисторсии прямиком по упрощенной Division Model с single term.
// K1 задано.
for Yd = -1 to 1  with enough small step  {
    for Xd = -1 to 1 with enough small step {      
       r = sqrt(Xd*Xd + Yd*Yd);
       Xu = Xd / (1 + K1 * r*r);
       Yu = Yd / (1 + K1 * r*r);
       Xu_ClippedScaled = Clip( width * Xu, 0, width);
       Yu_ClippedScaled = Clip( height * Yu, 0, height);
       plot ( Xu_ClippedScaled, Yu_ClippedScaled, 
                                                      GetPixel(width * Xd, height * Yd) );       
    }
}
// вместо plot() вестимо  StoreTableToMemory(....) .    
 
В конечном итоге для трансформации каждого кадра используется только очень быстрый код с вычисленной Transform-таблицей: 
   Target[index] = Src[ TransformTable[index] ] .
При этом таблица симметрична в квадрантах, т.е. сохранять можно всего один квадрант.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

16 hours ago, std said:

1. Возможно идеальное решение методом цифровой бработки сигнала и цифровой 2D фильтрации. С изучением исходной системы единичными функциями Дирака, сверткой, и получением полного отклика. Подготовкой требуемой матрицы 2D фильтра, и далее в каждом кадре покадровой быстрой сверткой. Но для микроконтроллеров это вычислительно слишком сложно. Поэтому обсуждать это далее бессмысленно.

Спасибо большое за советы! К сожалению, именно этот вариант и обсуждается, так как камера очень широкоугольная (фотки же я выше прилагал) и дисторсия простой моделью не описывается, а также нет гарантированного совмещения оси, то есть нормаль шахматной доски, нормаль поверхности сенсора и оптическая ось объектива - три не коллинеарные вектора и, скорей всего даже линейно независимые, то есть нужны степенные функции по x, y, в дополнении к степенным по r и проще иметь что-то типа 2Д сплайна.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, iiv said:

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

Третий способ, вручную, без к-л. математики, анализа и прочего.
Заработает везде и исправит любые искажения.

1. Вручную выставляем по клеточкам сетку вершин, для чего ваяем примитивный редактор, либо берем готовые софты.
2. Исходная картинка - текстура. Сетка вершин задана.
3. Софтварно текстурируем полигончики на экран в равномерную сетку.
Опционально, можем гладить сетку вершин сплайном непосредственно во время движения по сетке.

Произойдет (de)warping с вручную заданной картой варпа.
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо большое, std за ответ!

 

9 hours ago, std said:

1. Вручную выставляем по клеточкам сетку вершин, для чего ваяем примитивный редактор, либо берем готовые софты.

так я это уже запрограммировал, чтобы сделать полностью автоматически. У меня 24 камеры, для градуировки каждой надо снять хотя бы три кадра, а лучше с десяток. Представляете сколько я бы сидел карпел бы обрабатывая вручную каждый узел на шахматной доске в этих 240 картинках!!!

 

Честно говоря, все это есть в OpenCL, но все пришлось самому перезапрограммировать, так как OpenCL в eps32 ну ни как не лез.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...