Реконструкция изображения из субпиксельного вращения
Ищу способы сделать хайрезные картиночки на фотоматрицу с низким разрешением. Одна из идей - вращать объектив, получая субпиксельные сдвиги пикселей, которые потом склеить в большую картинку. Далее я опишу процесс симуляции этого действа.
Берем обычную картинку, это у нас будет реальная сцена:

Затем вращаем и уменьшаем ее как-то так:
for angle in {1..360}; do convert tree.jpg -gravity center -rotate $angle -crop 512x512+0+0 +repage -resize 32x32 low/$angle.jpg;done
Получаем на выходе кучу маленьких картинок:

Примерно такой датасет я планирую получить с реальной камеры, которая вращается.
Затем я делаю апскейл картинок:
for angle in {1..360}; do convert low/$angle.jpg -gravity center -rotate -$angle -crop 32x32+0+0 +repage -resize 512x512 hi/$angle.jpg;done
Возможно здесь уже кроется ошибка, так как при обрезке до 32 пикселей, возможно субпиксельное смещение изображения. Однако я делал похожий вариант с рендерингом векторной графики и тоже получил подобный результат.
Тем не менее, если "наивно" склеить все картинки, то получается мыло. Клей выглядит так:
naive.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define WIDTH 512
#define HEIGHT 512
#define PIXELS (WIDTH * HEIGHT)
#define BYTES (WIDTH * HEIGHT * 3)
int main(void)
{
uint32_t *sum = malloc(BYTES * 4);
uint8_t *inbuf = malloc(BYTES);
memset(sum, 0, BYTES * 4);
int q, count = 0;
while(1)
{
if(fread(inbuf, 1, BYTES, stdin) < 1)
{
break;
}
for(q = 0; q < BYTES; q++)
{
sum[q] += inbuf[q];
}
count++;
}
for(q = 0; q < BYTES; q++)
{
inbuf[q] = sum[q] / count;
}
fwrite(inbuf, 1, BYTES, stdout);
return 0;
}
Иными словами, простое сложение пикселей.
На выходе получаем:
Что далеко от начального варианта.
Вот еще вариант, но уже с рендерингом векторной информации:
Возможно, тут субпиксельная информация некорректна из-за алгоритма антиалиасинга
А вот результат:
Для сравнения, первый кадр, просто растянутый бикубиком:
Как видно, разницы почти нету. Вопрос: как реконструировать картинку, увеличивая разрешение? Современные методы супер-резолюшена основаны на нейросетях, а мне же нужны реальная информация из соседних кадров. В какую сторону двигаться мне?
Ответы (2 шт):
Первым делом нужно из RGB вытянуть реальные яркости.
https://coderoad.ru/596216/Формула-для-определения-яркости-цвета-RGB
// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;
// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
double c = ic/255.0;
if ( c <= 0.04045 )
return c/12.92;
else
return pow(((c+0.055)/(1.055)),2.4);
}
// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
if(v<=0.0031308)
v *= 12.92;
else
v = 1.055*pow(v,1.0/2.4)-0.055;
return int(v*255+0.5); // This is correct in C++. Other languages may not
// require +0.5
}
// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
return gam_sRGB(
rY*inv_gam_sRGB(r) +
gY*inv_gam_sRGB(g) +
bY*inv_gam_sRGB(b)
);
}
Далее написать функцию, которая будет выдавать от каких пикселей и с какими процентами зависит конкретный пиксель при повороте на определённый градус.
Математика жёсткая. Уравнений огромное количество.
В итоге будет три (R,G,B) задачи уравнений, решив которую вы получите результат. Матрицы будут очень большими. Потом не забыть результаты яркостей перевести в RGB.
Ленивый метод слишком ленивый. Если вращать самостоятельно картинки из датасета, то результат будет намного лучше. Я реализовал это в маткаде и выглядит уже лучше чем, что получилось у вас

Идея следующая: мы знаем углы у картинок в датасете и в цикле поворачиваем картинку обратно, по нехитрой формуле высчитываем "настоящие" координаты пикселя:
newX = xc + (x-xc) * cos + (y-yc) * sin
newY = yc + (y-yc) * cos - (x-xc) * sin,
где sin и cos от угла изображения
Новые координаты получаются дробными, но домножив на отношение исходного размера изображения к размеру картинок в сете, мы получаем бОльшую точность целой части. И таким образом заполняем наше новое изображение. Остальные, незакрашенные пиксели можно заполнить любым удобным вам образом, я воспользовался средним значением четырех соседей.
Исходное изображение:

Результат работы программы:
Датасет получен аналогичным образом, но картинки 128 на 128, всего 360 изображений. Изначальное изображение 512 на 512




