Minor fixes, new MergeDebevec
authorFedor Morozov <f-morozov@ya.ru>
Tue, 6 Aug 2013 11:24:01 +0000 (15:24 +0400)
committerFedor Morozov <f-morozov@ya.ru>
Tue, 6 Aug 2013 11:24:01 +0000 (15:24 +0400)
modules/highgui/src/grfmt_hdr.cpp
modules/photo/doc/hdr_imaging.rst
modules/photo/include/opencv2/photo.hpp
modules/photo/src/calibrate.cpp
modules/photo/src/hdr_common.cpp
modules/photo/src/merge.cpp
modules/photo/src/tonemap.cpp
modules/photo/test/test_hdr.cpp

index 8e6b86e..8d25fe9 100644 (file)
@@ -90,6 +90,7 @@ bool HdrDecoder::readData(Mat& _img)
        }
        RGBE_ReadPixels_RLE(file, const_cast<float*>(img.ptr<float>()), img.cols, img.rows);
        fclose(file); file = NULL;
+       
        if(_img.depth() == img.depth()) {
                img.convertTo(_img, _img.type());
        } else {
@@ -123,10 +124,16 @@ HdrEncoder::~HdrEncoder()
 
 bool HdrEncoder::write( const Mat& _img, const std::vector<int>& params )
 {
-       CV_Assert(_img.channels() == 3);
        Mat img;
-       if(_img.depth() != CV_32F) {
-               _img.convertTo(img, CV_32FC3, 1/255.0f);
+       CV_Assert(img.channels() == 3 || img.channels() == 1);
+       if(img.channels() == 1) {
+               std::vector<Mat> splitted(3, _img);
+                merge(splitted, img);
+       } else {
+               _img.copyTo(img);
+       }
+       if(img.depth() != CV_32F) {
+               img.convertTo(img, CV_32FC3, 1/255.0f);
        }
        CV_Assert(params.empty() || params[0] == HDR_NONE || params[0] == HDR_RLE);
        FILE *fout = fopen(m_filename.c_str(), "wb");
index 1e150ee..c5f989a 100644 (file)
@@ -193,7 +193,7 @@ Recovers camera response.
 
     :param src: vector of input images
     
-    :param dst: matrix with calculated camera response, one column per channel
+    :param dst: matrix with calculated camera response
     
     :param times: vector of exposure time values for each image
     
@@ -231,7 +231,7 @@ Merges images.
     
     :param times: vector of exposure time values for each image
     
-    :param response: matrix with camera response, one column per channel
+    :param response: one-column matrix with camera response
     
 MergeDebevec
 --------
index 8c709b3..c3c91b8 100644 (file)
@@ -206,6 +206,9 @@ public:
     
     CV_WRAP virtual int getSamples() const = 0;
     CV_WRAP virtual void setSamples(int samples) = 0;
+
+    CV_WRAP virtual bool getTest() const = 0;
+    CV_WRAP virtual void setTest(bool val) = 0;
 };
 
 CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 50, float lambda = 10.0f);
index 8a51f53..c8c51cb 100644 (file)
@@ -55,7 +55,8 @@ public:
         samples(samples),
         lambda(lambda),
         name("CalibrateDebevec"),
-        w(tringleWeights())
+        w(tringleWeights()),
+        test(false)
     {
     }
     
@@ -63,14 +64,19 @@ public:
     {
         std::vector<Mat> images;
         src.getMatVector(images);
-        dst.create(256, images[0].channels(), CV_32F);
-        Mat response = dst.getMat();
 
-        CV_Assert(!images.empty() && images.size() == times.size());
-        CV_Assert(images[0].depth() == CV_8U);
+        CV_Assert(images.size() == times.size());
         checkImageDimensions(images);
+        CV_Assert(images[0].depth() == CV_8U);
+
+        int channels = images[0].channels();
+        int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
 
-        for(int channel = 0; channel < images[0].channels(); channel++) {
+        dst.create(256, 1, CV_32FCC);
+        Mat result = dst.getMat();
+        
+        std::vector<Mat> result_split(channels);
+        for(int channel = 0; channel < channels; channel++) {
             Mat A = Mat::zeros(samples * images.size() + 257, 256 + samples, CV_32F);
             Mat B = Mat::zeros(A.rows, 1, CV_32F);
 
@@ -78,6 +84,9 @@ public:
             for(int i = 0; i < samples; i++) {
 
                 int pos = 3 * (rand() % images[0].total()) + channel;
+                if(test) {
+                    pos = 3 * i + channel;
+                }
                 for(size_t j = 0; j < images.size(); j++) {
 
                     int val = (images[j].ptr() + pos)[0];
@@ -98,11 +107,15 @@ public:
             }
             Mat solution;
             solve(A, B, solution, DECOMP_SVD);
-            solution.rowRange(0, 256).copyTo(response.col(channel));
+            solution.rowRange(0, 256).copyTo(result_split[channel]);
         }
-        exp(response, response);
+        merge(result_split, result);
+        exp(result, result);
     }
 
+    bool getTest() const { return test; }
+    void setTest(bool val) { test = val; }
+
     int getSamples() const { return samples; }
     void setSamples(int val) { samples = val; }
 
@@ -128,6 +141,7 @@ protected:
     String name;
     int samples;
     float lambda;
+    bool test;
     Mat w;
 };
 
index 4aab5c0..6e58a23 100644 (file)
@@ -61,11 +61,9 @@ void checkImageDimensions(const std::vector<Mat>& images)
 
 Mat tringleWeights()
 {
-    Mat w(256, 3, CV_32F);
+    Mat w(256, 1, CV_32F);
     for(int i = 0; i < 256; i++) {
-        for(int j = 0; j < 3; j++) {
-            w.at<float>(i, j) = i < 128 ? i + 1.0f : 256.0f - i;
-        }
+        w.at<float>(i) = i < 128 ? i + 1.0f : 256.0f - i;
     }
     return w;
 }
index b7fcac6..7f45059 100644 (file)
@@ -61,62 +61,87 @@ public:
     {
         std::vector<Mat> images;
         src.getMatVector(images);
-        dst.create(images[0].size(), CV_MAKETYPE(CV_32F, images[0].channels()));
-        Mat result = dst.getMat();
 
         CV_Assert(images.size() == times.size());
-        CV_Assert(images[0].depth() == CV_8U);
         checkImageDimensions(images);
+        CV_Assert(images[0].depth() == CV_8U);
+
+        int channels = images[0].channels();
+        Size size = images[0].size();
+        int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
+
+        dst.create(images[0].size(), CV_32FCC);
+        Mat result = dst.getMat();
 
         Mat response = input_response.getMat();
-        CV_Assert(response.rows == 256 && response.cols >= images[0].channels());
-        Mat log_response;
-        log(response, log_response);
-        
-        std::vector<float> exp_times(times.size());
-        for(size_t i = 0; i < exp_times.size(); i++) {
-            exp_times[i] = logf(times[i]);
+
+        if(response.empty()) {
+            response = linearResponse(channels);
         }
+        log(response, response);
+        CV_Assert(response.rows == 256 && response.cols == 1 && 
+                  response.channels() == channels);
+
+        Mat exp_values(times);
+        log(exp_values, exp_values);
     
-        int channels = images[0].channels();
-        float *res_ptr = result.ptr<float>();
-        for(size_t pos = 0; pos < result.total(); pos++, res_ptr += channels) {
+        result = Mat::zeros(size, CV_32FCC);
+        std::vector<Mat> result_split;
+        split(result, result_split);
+        Mat weight_sum = Mat::zeros(size, CV_32F);
 
-            std::vector<float> sum(channels, 0);
-            float weight_sum = 0;
-            for(size_t im = 0; im < images.size(); im++) {
+        for(size_t i = 0; i < images.size(); i++) {
+            std::vector<Mat> splitted;
+            split(images[i], splitted);
 
-                uchar *img_ptr = images[im].ptr() + channels * pos;
-                float w = 0;
-                for(int channel = 0; channel < channels; channel++) {
-                    w += weights.at<float>(img_ptr[channel]);
-                }
-                w /= channels; 
-                weight_sum += w;
-                for(int channel = 0; channel < channels; channel++) {
-                    sum[channel] += w * (log_response.at<float>(img_ptr[channel], channel) - exp_times[im]);
-                }
+            Mat w = Mat::zeros(size, CV_32F);
+            for(int c = 0; c < channels; c++) {
+                LUT(splitted[c], weights, splitted[c]);
+                w += splitted[c];
             }
-            for(int channel = 0; channel < channels; channel++) {
-                res_ptr[channel] = exp(sum[channel] / weight_sum);
+            w /= channels;
+
+            Mat response_img;
+            LUT(images[i], response, response_img);
+            split(response_img, splitted);
+            for(int c = 0; c < channels; c++) {
+                result_split[c] += w.mul(splitted[c] - exp_values.at<float>(i));
             }
+            weight_sum += w;
+        }
+        weight_sum = 1.0f / weight_sum;
+        for(int c = 0; c < channels; c++) {
+            result_split[c] = result_split[c].mul(weight_sum);
         }
+        merge(result_split, result);
+        exp(result, result);
     }
 
     void process(InputArrayOfArrays src, OutputArray dst, const std::vector<float>& times)
     {
-        Mat response(256, 3, CV_32F);
-        for(int i = 0; i < 256; i++) {
-            for(int j = 0; j < 3; j++) {
-                response.at<float>(i, j) = static_cast<float>(max(i, 1));
-            }
-        }
-        process(src, dst, times, response);
+        process(src, dst, times, Mat());
     }
 
 protected:
     String name;
     Mat weights;
+
+    Mat linearResponse(int channels)
+    {
+        Mat single_response = Mat(256, 1, CV_32F);
+        for(int i = 1; i < 256; i++) {
+            single_response.at<float>(i) = static_cast<float>(i);
+        }
+        single_response.at<float>(0) = static_cast<float>(1);
+
+        std::vector<Mat> splitted(channels);
+        for(int c = 0; c < channels; c++) {
+            splitted[c] = single_response;
+        }
+        Mat result;
+        merge(splitted, result);
+        return result;
+    }
 };
 
 Ptr<MergeDebevec> createMergeDebevec()
@@ -146,33 +171,48 @@ public:
         src.getMatVector(images);
         checkImageDimensions(images);
 
+        int channels = images[0].channels();
+        CV_Assert(channels == 1 || channels == 3);
+        Size size = images[0].size();
+        int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
+
         std::vector<Mat> weights(images.size());
-        Mat weight_sum = Mat::zeros(images[0].size(), CV_32FC1);
-        for(size_t im = 0; im < images.size(); im++) {
+        Mat weight_sum = Mat::zeros(size, CV_32F);
+
+        for(size_t i = 0; i < images.size(); i++) {
             Mat img, gray, contrast, saturation, wellexp;
-            std::vector<Mat> channels(3);
+            std::vector<Mat> splitted(channels);
 
-            images[im].convertTo(img, CV_32FC3, 1.0/255.0);
-            cvtColor(img, gray, COLOR_RGB2GRAY);
-            split(img, channels);
+            images[i].convertTo(img, CV_32F, 1.0f/255.0f);
+            if(channels == 3) {
+                cvtColor(img, gray, COLOR_RGB2GRAY);
+            } else {
+                img.copyTo(gray);
+            }
+            split(img, splitted);
 
             Laplacian(gray, contrast, CV_32F);
             contrast = abs(contrast);
 
-            Mat mean = (channels[0] + channels[1] + channels[2]) / 3.0f;
-            saturation = Mat::zeros(channels[0].size(), CV_32FC1);
-            for(int i = 0; i < 3;  i++) {
-                Mat deviation = channels[i] - mean;
-                pow(deviation, 2.0, deviation);
+            Mat mean = Mat::zeros(size, CV_32F);
+            for(int c = 0; c < channels; c++) {
+                mean += splitted[c];
+            }
+            mean /= channels;
+
+            saturation = Mat::zeros(size, CV_32F);
+            for(int c = 0; c < channels;  c++) {
+                Mat deviation = splitted[c] - mean;
+                pow(deviation, 2.0f, deviation);
                 saturation += deviation;
             }
             sqrt(saturation, saturation);
 
-            wellexp = Mat::ones(gray.size(), CV_32FC1);
-            for(int i = 0; i < 3; i++) {
-                Mat exp = channels[i] - 0.5f;
-                pow(exp, 2, exp);
-                exp = -exp / 0.08;
+            wellexp = Mat::ones(size, CV_32F);
+            for(int c = 0; c < channels; c++) {
+                Mat exp = splitted[c] - 0.5f;
+                pow(exp, 2.0f, exp);
+                exp = -exp / 0.08f;
                 wellexp = wellexp.mul(exp);
             }
 
@@ -180,33 +220,37 @@ public:
             pow(saturation, wsat, saturation);
             pow(wellexp, wexp, wellexp);
 
-            weights[im] = contrast;
-            weights[im] = weights[im].mul(saturation);
-            weights[im] = weights[im].mul(wellexp);
-            weight_sum += weights[im];
+            weights[i] = contrast;
+            if(channels == 3) {
+                weights[i] = weights[i].mul(saturation);
+            }
+            weights[i] = weights[i].mul(wellexp);
+            weight_sum += weights[i];
         }
-        int maxlevel = static_cast<int>(logf(static_cast<float>(max(images[0].rows, images[0].cols))) / logf(2.0)) - 1;
+        int maxlevel = static_cast<int>(logf(static_cast<float>(min(size.width, size.height))) / logf(2.0f));
         std::vector<Mat> res_pyr(maxlevel + 1);
 
-        for(size_t im = 0; im < images.size(); im++) {
-            weights[im] /= weight_sum;
+        for(size_t i = 0; i < images.size(); i++) {
+            weights[i] /= weight_sum;
             Mat img;
-            images[im].convertTo(img, CV_32FC3, 1/255.0);
+            images[i].convertTo(img, CV_32F, 1.0f/255.0f);
+            
             std::vector<Mat> img_pyr, weight_pyr;
             buildPyramid(img, img_pyr, maxlevel);
-            buildPyramid(weights[im], weight_pyr, maxlevel);
+            buildPyramid(weights[i], weight_pyr, maxlevel);
+
             for(int lvl = 0; lvl < maxlevel; lvl++) {
                 Mat up;
                 pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size());
                 img_pyr[lvl] -= up;
             }
             for(int lvl = 0; lvl <= maxlevel; lvl++) {
-                std::vector<Mat> channels(3);
-                split(img_pyr[lvl], channels);
-                for(int i = 0; i < 3; i++) {
-                    channels[i] = channels[i].mul(weight_pyr[lvl]);
+                std::vector<Mat> splitted(channels);
+                split(img_pyr[lvl], splitted);
+                for(int c = 0; c < channels; c++) {
+                    splitted[c] = splitted[c].mul(weight_pyr[lvl]);
                 }
-                merge(channels, img_pyr[lvl]);
+                merge(splitted, img_pyr[lvl]);
                 if(res_pyr[lvl].empty()) {
                     res_pyr[lvl] = img_pyr[lvl];
                 } else {
@@ -219,7 +263,7 @@ public:
             pyrUp(res_pyr[lvl], up, res_pyr[lvl - 1].size());
             res_pyr[lvl - 1] += up;
         }
-        dst.create(images[0].size(), CV_32FC3);
+        dst.create(size, CV_32FCC);
         res_pyr[0].copyTo(dst.getMat());
     }
 
index 9115b09..1fd0895 100644 (file)
@@ -185,7 +185,7 @@ class TonemapDurandImpl : public TonemapDurand
 public:
     TonemapDurandImpl(float gamma, float contrast, float saturation,  float sigma_color, float sigma_space) : 
         gamma(gamma), 
-               contrast(contrast),
+        contrast(contrast),
         saturation(saturation),
         sigma_color(sigma_color),
         sigma_space(sigma_space),
@@ -462,8 +462,8 @@ protected:
     void signedPow(Mat src, float power, Mat& dst)
     {
         Mat sign = (src > 0);
-        sign.convertTo(sign, CV_32F, 1/255.0f);
-        sign = sign * 2 - 1;
+        sign.convertTo(sign, CV_32F, 1.0f/255.0f);
+        sign = sign * 2.0f - 1.0f;
         pow(abs(src), power, dst);
         dst = dst.mul(sign);
     }
index 693eac7..b0a7e49 100644 (file)
@@ -78,11 +78,11 @@ void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times = DE
 
 void loadResponseCSV(String path, Mat& response)
 {
-       response = Mat(256, 3, CV_32F);
+       response = Mat(256, 1, CV_32FC3);
     ifstream resp_file(path.c_str());
        for(int i = 0; i < 256; i++) {
-               for(int channel = 0; channel < 3; channel++) {
-                       resp_file >> response.at<float>(i, channel);
+               for(int c = 0; c < 3; c++) {
+                       resp_file >> response.at<Vec3f>(i)[c];
                        resp_file.ignore(1);
                }
        }
@@ -187,6 +187,7 @@ TEST(Photo_MergeDebevec, regression)
        Mat result, expected;
        loadImage(test_path + "merge/debevec.exr", expected);
        merge->process(images, result, times, response);
+       imwrite("test.exr", result);
        checkEqual(expected, result, 1e-3f);
 }
 
@@ -199,9 +200,8 @@ TEST(Photo_CalibrateDebevec, regression)
        Mat expected, response;
        loadExposureSeq(test_path + "exposures/", images, times);
        loadResponseCSV(test_path + "calibrate/debevec.csv", expected);
-
        Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
-       srand(1);
+       calibrate->setTest(true);
        calibrate->process(images, response, times);
        checkEqual(expected, response, 1e-3f);
 }