Читать серию изображений построчно или все изображение для производительности? - PullRequest
1 голос
/ 28 марта 2011

Я пишу приложение, которое принимает серию экспозиций цели, вычисляет их среднее значение и сохраняет результирующее изображение.Этот метод широко используется в астрофотографии для уменьшения шума на конечном изображении.По сути, каждый вычисляет среднее значение в пикселях и записывает значение в выходной файл.

Количество экспозиций может быть довольно высоким, от 20 до 30 (иногда даже больше), и с современными большими датчиками CCDразрешение тоже может быть довольно высоким.Таким образом, объем данных может быть очень очень большим.

Мой вопрос: когда речь идет о производительности, следует ли мне читать изображения построчно (Метод # 1) или мне следует читать весь массив изображений всехмассивы (метод № 2)?Используя первый метод, мне нужно будет загрузить каждую соответствующую строку.Итак, если у меня есть 10 изображений, и я читаю строку № 1 - мне придется читать первую строку каждого изображения, вычислять их среднее значение и записывать строку.

С последним методом я читаювсе изображения целиком, вычисляют и записывают все изображение.

Теоретически, последний метод должен быть намного быстрее, но гораздо больше памяти.Однако на практике я обнаружил, что разница в производительности невелика, и это было удивительно.Самое большее, метод № 2 был всего на 2–3 секунды быстрее, чем метод № 1.Тем не менее, метод № 2 использовал до 1,3 ГБ памяти для 24 8-мегапиксельных изображений.Метод № 1, с другой стороны, максимально использует 70 МБ.В среднем оба метода занимают около 20 секунд для обработки 24 8-мегапиксельных изображений.

Я пишу это в Objective-C с хорошим количеством C, добавляемым при вызове CFITSIO.

Вот этот метод# 1:

pixelRows = (double**)malloc(self.numberOfImages * sizeof(double*)); //alloc. pixel array.
for(i=0;i<self.numberOfImages;i++)
{
    pixelRows[i] = (double*)malloc(width*sizeof(double));
}
apix = (double*)malloc(width*sizeof(double));
for(firstpix[1]=1;firstpix[1]<=size[1];firstpix[1]++)
{
    [self gatherRowsFromImages:firstpix[1] withRowWidth:theWidth thePixelMap:pixelRows];
    [self averageRows:pixelRows width:width theAveragedRow:apix];
    fits_write_pix(outfptr, TDOUBLE, firstpix, width,apix, &status);
    //NSLog(@"Row %ld written.",firstpix[1]);
}

fits_close_file(outfptr,&status);
NSLog(@"End");
if(!status)
{
    NSLog(@"File written successfully.");
}
for(i=0;i<self.numberOfImages;i++)
{
    free(pixelRows[i]);
}
free(pixelRows);
free(apix);

Вот метод № 2:

imageArray = (double**)malloc(files.count * sizeof(double*));
for(i=0;i<files.count;i++)
{
    imageArray[i] = (double*)malloc(size[0] * size[1] * sizeof(double));
    fits_read_pix(fptr[i],TDOUBLE,firstpix,size[0] * size[1],NULL,imageArray[i],NULL,&status);
    //NSLog(@"%d",status);
}
int fileIndex;

NSLog(@"%d",files.count);
apix = (double*)malloc(size[0] * size[1] * sizeof(double));
for(i=0;i<(size[0] * size[1]);i++)
{
    apix[i] = 0.0;
    for(fileIndex=0;fileIndex<files.count;fileIndex++)
    {
        apix[i] = apix[i] + imageArray[fileIndex][i];
    }
    //NSLog(@"%f",apix[i]);
    apix[i] = apix[i] / files.count;
}

fits_create_file(&outfptr,[outPath UTF8String],&status);
fits_copy_header(fptr[0],outfptr,&status);
fits_write_pix(outfptr, TDOUBLE, firstpix, size[0] * size[1],apix, &status);
fits_close_file(outfptr,&status);

Есть предложения по этому поводу?Ожидаю ли я слишком большой выгоды от прочтения каждого изображения целиком?

1 Ответ

1 голос
/ 28 марта 2011

Я бы всегда использовал построчный подход, поскольку он масштабируемый. Это также может быть быстрее, поскольку объем занимаемой памяти меньше, а это означает, что нет необходимости выгружать какую-либо программу на диск только для инструмента, потребляющего память.

Кроме того, для оптимизации построчного подхода следует также рассмотреть возможность считывания изображений по 8 строкам (или некоторым другим числам). Например. JPEG хранится в блоках 8x8, поэтому чтение в менее чем 8 строк будет бессмысленным. Конечно, это зависит от формата изображения и используемой вами библиотеки.

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

Еще одна вещь, которую следует учитывать, - это код, который выполняет фактическое усреднение. Настройка этого также принесет большую пользу, особенно для той операции, которую вы выполняете, посмотрите SSE и смежные темы. Также использование целочисленных вычислений, вероятно, превзойдет арифметику с плавающей точкой. Использование сдвигов битов для деления также может быть быстрее, чем истинное деление, но оно позволит вам делить только на 2 ^ n.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...