Нужна помощь в векторизации этого кода

У меня есть 8-битное изображение. Для каждого пикселя мне нужно определить его порядковое положение в текущей строке. Например, если строка:

32 128 16 64,

тогда мне нужен результат:

1 3 0 2,

так как 32 является 1-м по величине значением в строке, 128 — 3-м по величине, 16 — 0-м по величине и 64 — 2-м по величине.

Мне нужно повторить описанную выше процедуру для всех строк изображения. Вот не векторизованный код:

for (int curr = 0; curr < new_height; ++curr)
{
    vector<pair<unsigned char, char> > ordered;
    for (char i = 0; i < 4; ++i)
    {
        unsigned char val = luma24.at<unsigned char>(curr, i);
        ordered.push_back(pair<unsigned char, char>(val, i));
    }
    sort(ordered.begin(), ordered.end(), cmpfun);
    for (int i = 0; i < 4; ++i)
        signature.at<char>(curr, ordered[i].second) = i;
}

luma24 — это 8-битное изображение, из которого я читаю, и оно имеет new_height строк и 4 столбца. signature - это подписанное изображение того же размера (пока не обращайте внимания на разницу в знаке, поскольку это не имеет значения) - здесь я сохраняю результат. cmpfun — тривиальная функция сравнения.

Я попытался векторизовать приведенный выше код и получил это:

Mat ordinal;
luma24.convertTo(ordinal, CV_16UC1, 256, 0);
Mat sorted = ordinal.clone();
for (int i = 0; i < 4; ++i)
    ordinal(Range::all(), Range(i, i+1)) += i;
cv::sort(ordinal, sorted, CV_SORT_EVERY_ROW | CV_SORT_ASCENDING);
bitwise_and(sorted, Scalar(0x00ff), ordinal);
Mat ordinal8;
ordinal.convertTo(ordinal8, CV_8SC1, 1, 0);
ordinal8.copyTo(signature(Range::all(), Range(0, 4)));

Мне пришлось упаковать 8-битное значение и 8-битный порядковый номер в один 16-битный канал, поскольку OpenCV не выполняет сортировку многоканальных изображений. Это почти то, что мне нужно, но не совсем. Для примера ввода это дает мне:

2 0 3 1

поскольку наименьшее значение находится во 2-м столбце, следующее наименьшее — в 0-м столбце и т. д. Как мне преобразовать это в нужный мне результат без доступа к каждому пикселю по отдельности?

По сути, мне нужно как-то векторизовать это:

uint8_t x[] = {2, 0, 3, 1};
uint8_t y[4];
for (uint8_t i = 0; i < 4; ++i)
    y[x[i]] = i;

где x — промежуточный результат, который дает мой текущий векторизованный код, а y — результат, который я хочу.

Можно ли это сделать?


person mpenkov    schedule 12.03.2013    source источник
comment
Просто для уточнения (у меня пока нет ответа). Что вы хотите сделать, если у вас есть несколько пикселей с одинаковым значением? Должны ли они все быть одного и того же порядкового номера?   -  person Roger Rowland    schedule 12.03.2013
comment
Не по теме: какое совпадение, буквально на днях я читал исходный код ffmpeg, который вы было зеркало на github. URL-адрес перестал работать, поэтому я зашел в ваш профиль на случай, если вы его переименовали, но я думаю, вы удалили его, и прямо сейчас я случайно узнал ваш аватар.   -  person Jorge Israel Peña    schedule 12.03.2013
comment
В таком виде это почти невозможно. Какие существуют ограничения? например x[] всегда имеет ширину 4 элемента? вместо этого должно быть uint8_t?   -  person Aki Suihkonen    schedule 12.03.2013
comment
@roger_rowland: нет, порядковые значения должны быть уникальными, даже если есть несколько пикселей с одинаковым значением. @ Хорхе Исраэль Пенья: кто-то еще присматривает за этим в данный момент. Посмотрите ffmpeg.org/documentation.html для получения обновленной ссылки. @ Аки Суихконен: нет, это не всегда будет четыре элемента шириной. Да, uint8_t лучше -- мне просто было лень.   -  person mpenkov    schedule 12.03.2013
comment
Разве вам не нужно просто cv:sortIdx()?   -  person Adi Shavit    schedule 24.04.2013


Ответы (2)


Я верю, что это поможет вам. Он не требует распределения, стеков или сортировки, но предполагает, что ваш диапазон равен 0-255 (например, uint8). Более широкое предположение: это будет эффективно, только если у вас есть широкие строки. Если они действительно имеют ширину 4 пикселя, то i‹256 выглядит довольно уродливо. Есть способы избавиться от этого, но я предполагаю, что 4 пикселя — это просто «например». для простоты.

void processRow (int* rowpos, uint8_t* pixelsForRow, int w) {
   uint32_t i, pv, v=0, hist[256]={0};
   for (i=0; i<w; i++)      hist[pixelsForRow[i]]++;
   for (i=0; i<256; i++)    {pv=hist[i]; hist[i]=v; v+=pv;}
   for (i=0; i<w; i++)      rowpos[i] = hist[pixelsForRow[i]]++;
}

Итак, как это работает?
Строка 1 в этой функции объявляет и очищает таблицу гистограммы.
Строка 2 вычисляет гистограмму.
Строка 3 превращает ее в сортировку с подсчетом – и вот почему hist использует больший размер элемента, чем uint8
строка 4 применяет отсортированную позицию.

Есть 2 трюка; Во-первых, в строке 3 гистограммы «смещены на 1 индекс», так что первое значение всегда равно «0», а не тому, что было бы, а второе значение — это то, каким был бы первый счет, и так далее. Второй прием — это "++" в строке 4. Он всегда обеспечивает уникальность порядкового номера.

Давайте попробуем применить его к вашему вводу:
[32 128 16 64]
строка 2: [0...1....1....1...1...0] с индексами [0, 16, 32, 64, 128, 255] соответственно
строка 3: [0...0....1....2...3...0] с индексами [0, 16, 32, 64, 128, 255] соответственно
строка 4: [1, 3, 0, 2] ... выглядит правильно

Давайте попробуем немного изменить ввод:
[32 128 16 32]
строка 2: [0. ..1....2....0...1...0] по индексам [0, 16, 32, 64, 128, 255] соответственно
строка 3: [0... 0....1....3...3...0] по индексам [0, 16, 32, 64, 128, 255] соответственно
строка 4: [1, 3, 0, 2] ... идеально


но я не совсем уверен, соответствует ли это вашим потребностям в векторизации -- :)

person sree    schedule 17.03.2013

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

Каждый элемент узла представляет собой структуру

// Members of struct explained here.
// row_pos: stores position of that pixel in that row.
//     we populate this while creating binary search tree. 
//
// rank: stores its rank in that row. ()
//  while doing in-order traversal, we come to know rank of that pixel. At that point only, we update that pixel location with its rank.

typedef struct node
{
    int row_pos, rank; 
    node *left, *right;    // left and right nodes.
};

последовательность шагов для каждой строки будет:

а) O(w): создать бинарное дерево поиска, сохраняя положение каждого пикселя также в узле.

б) O(w): начать обход по порядку. Для каждого узла заполните местоположение этого узла в пикселях рангом (начните считать с первого узла как 0).

person vijay.nidumolu    schedule 02.04.2013