Отображение в широком динамическом диапазоне с использованием openCV на iOS дает искаженный вывод

Я пытаюсь использовать openCV 3 на iOS для создания HDR-изображения из нескольких экспозиций, которое в конечном итоге будет выводиться в виде файла EXR. Я заметил, что получал искаженный вывод, когда пытался создать изображение HDR. Думая, что попытка создать отклик камеры была ошибкой, я начал с нуля и адаптировал учебный материал по HDR-изображениям на openCV для iOS, но он дает схожие результаты. Следующий код C++ возвращает искаженное изображение:

cv::Mat mergeToHDR (vector<Mat>& images, vector<float>& times)
{
    imgs = images;
    Mat response;
    //Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
    //calibrate->process(images, response, times);

    Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson();
    calibrate->process(images, response, times);

    // create HDR
    Mat hdr;
    Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
    merge_debevec->process(images, hdr, times, response);

    // create LDR
    Mat ldr;
    Ptr<TonemapDurand> tonemap = createTonemapDurand(2.2f);
    tonemap->process(hdr, ldr);

    // create fusion
    Mat fusion;
    Ptr<MergeMertens> merge_mertens = createMergeMertens();
    merge_mertens->process(images, fusion);

    /*
    Uncomment what kind of tonemapped image or hdr to return
    Returning one of the images in the array produces ungarbled output
    so we know the problem is unlikely with the openCV to UIImage conversion
    */

    //give back one of the images from the image array
    //return images[0];

    //give back one of the hdr images
    return fusion * 255;
    //return ldr * 255;
    //return hdr
}

Вот как выглядит изображение:

Плохой вывод изображения

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

Каркас openCV - это последняя скомпилированная версия 3.0.0 с сайта openCV.org. RC и alpha дают одинаковые результаты, и текущая версия не будет собираться (для iOS или OSX). Я думал, что мои следующие шаги будут состоять в том, чтобы попытаться заставить фреймворк компилироваться с нуля, или заставить пример работать под другой платформой, чтобы увидеть, является ли проблема специфичной для платформы или с самими функциями openCV HDR. Но прежде чем сделать это, я подумал, что я подниму проблему из-за переполнения стека, чтобы увидеть, сталкивался ли кто-нибудь с той же проблемой или я упускал что-то ослепительно очевидное.

Я загрузил пример проекта xcode сюда:

https://github.com/artandmath/openCVHDRSwiftExample

Получение openCV для работы со swift было с помощью пользователя Foundry на Github.

1 ответ

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

Вот результирующее изображение:

Ожидаемый результат!

Вот фиксированная функция:

cv::Mat mergeToHDR (vector<Mat>& images, vector<float>& times)
{
    imgs = images;
    Mat response;
    //Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
    //calibrate->process(images, response, times);

    Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson();
    calibrate->process(images, response, times);

    // create HDR
    Mat hdr;
    Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
    merge_debevec->process(images, hdr, times, response);

    // create LDR
    Mat ldr;
    Ptr<TonemapDurand> tonemap = createTonemapDurand(2.2f);
    tonemap->process(hdr, ldr);

    // create fusion
    Mat fusion;
    Ptr<MergeMertens> merge_mertens = createMergeMertens();
    merge_mertens->process(images, fusion);

    /*
     Uncomment what kind of tonemapped image or hdr to return
     Convert back to 8-bits per channel because that is what
     the UIImage+OpenCV class extension is expecting
    */


    // tone mapped
    /*
    Mat ldr8bit;
    ldr = ldr * 255;
    ldr.convertTo(ldr8bit, CV_8U);
    return ldr8bit;
    */

    // fusion
    Mat fusion8bit;
    fusion = fusion * 255;
    fusion.convertTo(fusion8bit, CV_8U);
    return fusion8bit;

    // hdr
    /*
    Mat hdr8bit;
    hdr = hdr * 255;
    hdr.convertTo(hdr8bit, CV_8U);
    return hdr8bit;
    */
}

В качестве альтернативы вот исправление для метода initWithCVMat в расширении класса OpenCV+UIImage на основе одного из руководств по iOS в разделе iOS на opencv.org:

http://docs.opencv.org/2.4/doc/tutorials/ios/image_manipulation/image_manipulation.html

При создании нового CGImageRef с данными с плавающей запятой необходимо явно указать, что он ожидает данные с плавающей запятой, и порядок байтов данных изображения из openCV должен быть обратным. Теперь у iOS/Quartz есть данные с плавающей точкой! Это немного хакерское исправление, потому что метод все еще работает только с 8 или 32 битами на канал или альфа и не учитывает все виды изображений, которые могут быть переданы из Mat в UIImage.

- (id)initWithCVMat:(const cv::Mat&)cvMat
{
    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];
    CGColorSpaceRef colorSpace;

    size_t elemSize = cvMat.elemSize();
    size_t elemSize1 = cvMat.elemSize1();

    size_t channelCount = elemSize/elemSize1;
    size_t bitsPerChannel = 8 * elemSize1;
    size_t bitsPerPixel = bitsPerChannel * channelCount;

    if (channelCount == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }

    // Tell CGIImageRef different bitmap info if handed 32-bit
    uint32_t bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;

    if (bitsPerChannel == 32 ){
        bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapFloatComponents | kCGBitmapByteOrder32Little;
    }

    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                 //width
                                        cvMat.rows,                                 //height
                                        bitsPerChannel,                             //bits per component
                                        bitsPerPixel,                               //bits per pixel
                                        cvMat.step[0],                              //bytesPerRow
                                        colorSpace,                                 //colorspace
                                        bitmapInfo,                                 // bitmap info
                                        provider,                                   //CGDataProviderRef
                                        NULL,                                       //decode
                                        false,                                      //should interpolate
                                        kCGRenderingIntentDefault                   //intent
                                        );                     

    // Getting UIImage from CGImage
    self = [self initWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    return self;
}
Другие вопросы по тегам