Дискретное целое вейвлет-преобразование Daub 5/3 лифтинга
Я пытаюсь запустить целочисленное лифтинг 5/3 на изображении Лены. Я следил за работой Уолкера, Нгуена и Чена "Система с низким энергопотреблением и малой памятью для вейвлет-сжатия" ( ссылка активна с 7 октября 2015 г.).
Я сталкиваюсь с проблемами, хотя. Кажется, что изображение не совсем правильное. Я, кажется, немного переполнен в зеленом и синем каналах, что означает, что последующие проходы вейвлет-функции находят высокие частоты там, где их не должно быть. Я также почти уверен, что у меня что-то не так, поскольку я вижу линию изображения s0 по краям высокочастотных частей.
Моя функция заключается в следующем:
bool PerformHorizontal( Col24* pPixelsIn, Col24* pPixelsOut, int width, int pixelPitch, int height )
{
const int widthDiv2 = width / 2;
int y = 0;
while( y < height )
{
int x = 0;
while( x < width )
{
const int n = (x) + (y * pixelPitch);
const int n2 = (x / 2) + (y * pixelPitch);
const int s = n2;
const int d = n2 + widthDiv2;
// Non-lifting 5 / 3
/*pPixelsOut[n2 + widthDiv2].r = pPixelsIn[n + 2].r - ((pPixelsIn[n + 1].r + pPixelsIn[n + 3].r) / 2) + 128;
pPixelsOut[n2].r = ((4 * pPixelsIn[n + 2].r) + (2 * pPixelsIn[n + 2].r) + (2 * (pPixelsIn[n + 1].r + pPixelsIn[n + 3].r)) - (pPixelsIn[n + 0].r + pPixelsIn[n + 4].r)) / 8;
pPixelsOut[n2 + widthDiv2].g = pPixelsIn[n + 2].g - ((pPixelsIn[n + 1].g + pPixelsIn[n + 3].g) / 2) + 128;
pPixelsOut[n2].g = ((4 * pPixelsIn[n + 2].g) + (2 * pPixelsIn[n + 2].g) + (2 * (pPixelsIn[n + 1].g + pPixelsIn[n + 3].g)) - (pPixelsIn[n + 0].g + pPixelsIn[n + 4].g)) / 8;
pPixelsOut[n2 + widthDiv2].b = pPixelsIn[n + 2].b - ((pPixelsIn[n + 1].b + pPixelsIn[n + 3].b) / 2) + 128;
pPixelsOut[n2].b = ((4 * pPixelsIn[n + 2].b) + (2 * pPixelsIn[n + 2].b) + (2 * (pPixelsIn[n + 1].b + pPixelsIn[n + 3].b)) - (pPixelsIn[n + 0].b + pPixelsIn[n + 4].b)) / 8;*/
pPixelsOut[d].r = pPixelsIn[n + 1].r - (((pPixelsIn[n].r + pPixelsIn[n + 2].r) >> 1) + 127);
pPixelsOut[s].r = pPixelsIn[n].r + (((pPixelsOut[d - 1].r + pPixelsOut[d].r) >> 2) - 64);
pPixelsOut[d].g = pPixelsIn[n + 1].g - (((pPixelsIn[n].g + pPixelsIn[n + 2].g) >> 1) + 127);
pPixelsOut[s].g = pPixelsIn[n].g + (((pPixelsOut[d - 1].g + pPixelsOut[d].g) >> 2) - 64);
pPixelsOut[d].b = pPixelsIn[n + 1].b - (((pPixelsIn[n].b + pPixelsIn[n + 2].b) >> 1) + 127);
pPixelsOut[s].b = pPixelsIn[n].b + (((pPixelsOut[d - 1].b + pPixelsOut[d].b) >> 2) - 64);
x += 2;
}
y++;
}
return true;
}
Определенно что-то не так, но я просто не могу понять. Может ли кто-нибудь с немного большим количеством мозга, чем я, указать, где я иду не так? Стоит отметить, что вы можете увидеть неотнятую версию Daub 5/3 над рабочим кодом, и это также дает мне те же артефакты... Я очень запутался, поскольку у меня это уже работало однажды (было более 2 лет назад, и у меня больше нет этого кода).
Любая помощь приветствуется:)
Изменить: я, кажется, устранил мои проблемы переполнения, зажав пиксели нижних частот в диапазоне от 0 до 255. Я немного обеспокоен тем, что это не правильное решение. Кто-нибудь может прокомментировать это?
3 ответа
ОК, я могу без потерь пересылать, а затем инвертировать, пока я сохраняю свои данные пост-трансформации в коротком виде. Очевидно, это занимает немного больше места, чем я ожидал, но это позволяет мне стать хорошей отправной точкой для изучения различных алгоритмов сжатия. Также вы можете сжимать по 2 4 пикселя за один раз, используя инструкции SSE2. Это стандартное C-прямое преобразование, которое я придумал:
const int16_t dr = (int16_t)pPixelsIn[n + 1].r - ((((int16_t)pPixelsIn[n].r + (int16_t)pPixelsIn[n + 2].r) >> 1));
const int16_t sr = (int16_t)pPixelsIn[n].r + ((((int16_t)pPixelsOut[d - 1].r + dr) >> 2));
const int16_t dg = (int16_t)pPixelsIn[n + 1].g - ((((int16_t)pPixelsIn[n].g + (int16_t)pPixelsIn[n + 2].g) >> 1));
const int16_t sg = (int16_t)pPixelsIn[n].g + ((((int16_t)pPixelsOut[d - 1].g + dg) >> 2));
const int16_t db = (int16_t)pPixelsIn[n + 1].b - ((((int16_t)pPixelsIn[n].b + (int16_t)pPixelsIn[n + 2].b) >> 1));
const int16_t sb = (int16_t)pPixelsIn[n].b + ((((int16_t)pPixelsOut[d - 1].b + db) >> 2));
pPixelsOut[d].r = dr;
pPixelsOut[s].r = sr;
pPixelsOut[d].g = dg;
pPixelsOut[s].g = sg;
pPixelsOut[d].b = db;
pPixelsOut[s].b = sb;
Тривиально создать обратное этому (ОЧЕНЬ простой бит алгебры). Стоит отметить, что вам нужно инвертировать изображение справа налево снизу вверх. Затем я посмотрю, смогу ли я шунтировать эти данные в uint8_ts и потерять немного или 2 точности. Для сжатия это действительно не проблема.
Вы можете сделать несколько тестов с экстремальными значениями, чтобы увидеть возможность переполнения. Пример:
pPixelsOut[d].r = pPixelsIn[n + 1].r - (((pPixelsIn[n].r + pPixelsIn[n + 2].r) >> 1) + 127);
If:
pPixelsIn[n ].r == 255
pPixelsIn[n+1].r == 0
pPixelsIn[n+2].r == 255
Then:
pPixelsOut[d].r == -382
But if:
pPixelsIn[n ].r == 0
pPixelsIn[n+1].r == 255
pPixelsIn[n+2].r == 0
Then:
pPixelsOut[d].r == 128
У вас есть диапазон из 511 возможных значений (-382 .. 128), поэтому, чтобы избежать переполнения или ограничения, вам потребуется один дополнительный бит, некоторое квантование или другой тип кодирования!
Я предполагаю, что данные уже были пороговыми?
Я также не понимаю, почему вы добавляете обратно в +127 и -64.