Отображение в широком динамическом диапазоне с использованием 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;
}