У меня есть фрагмент кода C, который является частью общедоступного репозитория (Darknet), который должен изменять размер изображение с помощью билинейной интерполяции. Из-за того, как остальная часть кода работает с изображениями, изображение сохраняется в виде одномерного массива, где значения пикселей из исходного 3-канального изображения считываются шаг за шагом. Таким образом, значение, соответствующее пикселю (x, y, k)
(x: столбец, y: строка, k: канал), сохраняется в ячейке x + w.h + w.h.c
в одномерном массиве.
Функция изменения размера, которая на самом деле является частью Darknet, занимает значительное количество времени на этапе предварительной обработки, возможно, из-за вложенных циклов for, которые перебирают строки и столбцы и пытаются получить доступ к соответствующим значениям, а также, возможно, к типу преобразования: поэтому я пытаюсь создать более оптимизированную версию. Исходный код для изменения размера выглядит следующим образом. im
— исходное изображение, поэтому im.w
и im.h
— исходная ширина и высота. w
и h
— целевые ширина и высота.
image resize_image(image im, int w, int h)
{
image resized = make_image(w, h, im.c);
image part = make_image(w, im.h, im.c);
int r, c, k;
float w_scale = (float)(im.w - 1) / (w - 1);
float h_scale = (float)(im.h - 1) / (h - 1);
for(k = 0; k < im.c; ++k){
for(r = 0; r < im.h; ++r){
for(c = 0; c < w; ++c){
float val = 0;
if(c == w-1 || im.w == 1){
val = get_pixel(im, im.w-1, r, k);
} else {
float sx = c*w_scale;
int ix = (int) sx;
float dx = sx - ix;
val = (1 - dx) * get_pixel(im, ix, r, k) + dx * get_pixel(im, ix+1, r, k);
}
set_pixel(part, c, r, k, val);
}
}
}
for(k = 0; k < im.c; ++k){
for(r = 0; r < h; ++r){
float sy = r*h_scale;
int iy = (int) sy;
float dy = sy - iy;
for(c = 0; c < w; ++c){
float val = (1-dy) * get_pixel(part, c, iy, k);
set_pixel(resized, c, r, k, val);
}
if(r == h-1 || im.h == 1) continue;
for(c = 0; c < w; ++c){
float val = dy * get_pixel(part, c, iy+1, k);
add_pixel(resized, c, r, k, val);
}
}
}
free_image(part);
return resized;
}
Есть ли способ сделать эту функцию быстрее: например, создав более оптимизированный способ доступа к пикселям вместо этого пошагового чтения? Также отмечу здесь, что в моем случае:
Размеры исходного изображения и изображения с измененным размером будут фиксированными, поэтому моя «пользовательская» функция изменения размера не обязательно должна быть независимой от размера. Я перехожу от 640x360 к размерам 626x352.
Целевая платформа — NVIDIA Jetson с процессором ARM, поэтому такие инструкции, как AVX2, в моем случае неприменимы. Но у меня есть доступ к CUDA.
Здесь я должен отметить, что из-за требований моего проекта эта функция изменения размера на самом деле является частью библиотеки (.so), которая вызывается из Python. Таким образом, я не могу хранить что-либо «в памяти» как таковое, например, объекты текстур CUDA и т. д., поэтому их повторное создание может фактически создать дополнительные накладные расходы на стороне CUDA.
Любые предложения по улучшению этой рутины были бы очень полезны.
get_pixel
и co не очень эффективны, если вы можете, вам следует избегать их использования и обращаться кimage->data
только с одной переменной, которую вы увеличиваете. Структура изображения также должна использовать FAM для увеличения производительности. - person Stargateur   schedule 26.04.2018