Halide: приведение изображений RGB и распараллеливание размытия

Следующий код адаптирован из учебников Halide.

Func blurX(Func continuation)
{ Var x("x"), y("y"), c("c");
Func input_16("input_16");
input_16(x, y, c) = cast<uint16_t>(continuation(x, y, c));
Func blur_x("blur_x");
blur_x(x, y, c) = (input_16(x-1, y, c) +
2 * input_16(x, y, c) +
input_16(x+1, y, c)) / 4;
Func output("outputBlurX");
output(x, y, c) = cast<uint8_t>(blur_x(x, y, c));
return output;
}

int main()
{ Var x("x"), y("y"), c("c");
Image<uint8_t> input = load_image("input.png");
Func clamped("clamped");
clamped = BoundaryConditions::repeat_edge(input);
Func img1Fun("img1Fun");
Func img2Fun = blurX(clamped);
Func outputFun("outputFun");
/* carry on */
}

У меня три вопроса:

  1. Кастинг Является ли актерский состав cast<uint16_t>(clamped(x, y, c)) приведение 8-битных значений R G и B в каждой позиции (x, y) к 16-битному целому числу, то есть то, что возвращает приведение, является изображением RGB, которое можно проиндексировать, например, img1Fun (x, y, 0) для доступа к его значению R? Или это приведение каждого пикселя RGB в изображении к его значению яркости между [0..1] для пикселя RGB в каждой (x, y) позиции, т.е. r*0.3 + g*0.59 + b*0.11?

  2. Перегрузка RGB размытия являются арифметическими операциями на (x,y,c) перегружен по всем показателям? Например.

(input_16(x-1, y, c) + 2 * input_16(x, y, c) + input_16(x+1, y, c)) / 4;

Это перегрузка:

(input_16(x-1, y, 0) + 2 * input_16(x, y, 0) + input_16(x+1, y, 0)) / 4;
(input_16(x-1, y, 1) + 2 * input_16(x, y, 1) + input_16(x+1, y, 1)) / 4;
(input_16(x-1, y, 2) + 2 * input_16(x, y, 2) + input_16(x+1, y, 2)) / 4;
  1. Parallelising как я мог распараллелить blurX? На основе brighten.cpp пример из CVPR’15 Вот, Я мог бы использовать blur_x.vectorize(x, 4).parallel(y); векторизовать строку по оси X, распараллеливая потоки в направлении Y .. как это?
Func blurX(Func continuation)
{ Var x("x"), y("y"), c("c");
Func input_16("input_16");
input_16(x, y, c) = cast<uint16_t>(continuation(x, y, c));
Func blur_x("blur_x");
blur_x(x, y, c) = (input_16(x-1, y, c) +
2 * input_16(x, y, c) +
input_16(x+1, y, c)) / 4;
blur_x.vectorize(x, 4).parallel(y);
Func output("outputBlurX");
output(x, y, c) = cast<uint8_t>(blur_x(x, y, c));
return output;
}

1

Решение

Вопрос 1: Func определяет абстрактное отображение из набора координат в Expr, который является математической функцией этих координат. В общем, операторы просты и не имеют какого-либо специфического поведения при отображении, такого как преобразование цветового кортежа в скаляр светимости. (Чтобы выполнить такое преобразование, необходимо написать код, поскольку коэффициенты зависят от используемого цветового пространства.)

Отсюда и утверждение:

img1Fun(x, y, c) = cast<uint16_t>(clamped(x, y, c));

определяет input_16 с тем же количеством каналов, что и clamped но 16-битный тип вместо 8-битного типа. Арифметика в Halide остается с той же самой битовой шириной, что и его самый большой операнд, и в отличие от C неявно преобразуется в стандартный размер int. Это связано с тем, что при векторизации важно поддерживать явный контроль над размером полосы движения. В этом случае использование 16-разрядного промежуточного типа требуется, чтобы избежать переполнения при суммировании 8-разрядных значений.

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

Вопрос 2: фактически тот же ответ. Я бы не использовал здесь термин «перегрузка», но это определение применимо ко всем координатам. Var «c» упоминается с левой и правой стороны и имеет одинаковое значение на каждом. (У нас есть сокращенное обозначение подчеркивания (‘_’), означающее «ноль или более координат», позволяющее проходить через список аргументов, но в остальном в этих определениях нет ничего особенного.)

Вопрос 3: Самый простой способ запланировать это для векторизации и распараллеливания — это использовать планарную компоновку (все значения R хранятся рядом друг с другом, затем все G и т. Д.) И векторизоваться до соответствующего размера для 16-битной математики. , (Например, «vectorize (x, natural_vector_size ())» id работает внутри генератора.) Параллелизм потока по строкам — «.parallel (y)». В зависимости от длины строк, вы можете захотеть добавить разделенные параметры в директиву Parallel.

Этот график также будет работать с полуплоскостным представлением (строка R, строка G и строка B).

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

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]