首页 > 基于C++的本征图像分解(Intrinsic Image Decomposition)

基于C++的本征图像分解(Intrinsic Image Decomposition)

利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。
Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分。
Shading Image 是反应原图像光照情况的图像部分。

本文提供的算法是比较传统的,效果虽然不SOTA,但是可以满足要求不高的朋友,可以考虑用CUDA实现加速。

//main.cpp#include "lum_retinex.h"
#include const float threshold = 0.13;int main(int argc, char* argv[])
{ cv::Mat input = cv::imread("Input.png", 1);input.convertTo(input, CV_32FC3);int w = input.cols;int h = input.rows;cv::Mat reflectance(h, w, CV_32FC3);cv::Mat shading(h, w, CV_32FC1);lum::retinex_decomp rdecomp(w, h);rdecomp.solve_rgb(threshold, (const float*)input.data, (float*)reflectance.data, (float*)shading.data);cv::imshow("input", input);cv::imshow("shading", shading);cv::imshow("reflectance", reflectance);cv::Mat out(h, w, CV_32FC3);for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { out.at<cv::Vec3f>(i, j)[0] = reflectance.at<cv::Vec3f>(i, j)[0] * 255.0;out.at<cv::Vec3f>(i, j)[1] = reflectance.at<cv::Vec3f>(i, j)[1] * 255.0;out.at<cv::Vec3f>(i, j)[2] = reflectance.at<cv::Vec3f>(i, j)[2] * 255.0;}}cv::imwrite("img/reflectance1.png", out);cv::waitKey(0);return 0;
}
//lum_retinex.h#ifndef LUM_RETINEX_H
#define LUM_RETINEX_H#include 
#include namespace lum { // Run retinex as single function call (cannot reuse decomposition).void retinex(float threshold, const float* img, int w, int h, float* reflectance, float* shading);// First create decomposition for linear solver (slow)// Then solve for various right-hand-sides (fast)class retinex_decomp { public:retinex_decomp(int w, int h);void solve(float threshold, const float* img, float* refl, float* shading);void solve_rgb(float threshold, const float* img, float* refl, float* shading);private:int m_w;int m_h;using SpMat = Eigen::SparseMatrix < float > ;SpMat m_At;Eigen::SimplicialCholesky<SpMat> m_solver;};
}
#endif
// retinextest.cpp #include "lum_retinex.h"
#include 
#include 
#include namespace lum { // timing helperdouble get_s() { using namespace std::chrono;auto now = system_clock::now();system_clock::duration tse = now.time_since_epoch();return duration_cast<nanoseconds>(tse).count() / 1e9;}// Helper image processing routinesvoid log(const float* inimg, float* outimg, int sz) { 
#ifdef USE_MKLvsLn(sz, inimg, outimg);
#elsefor (int i = 0; i < sz; ++i) { float in = inimg[i];outimg[i] = std::logf(inimg[i] + 0.00001);}
#endif}void exp(const float* inimg, float* outimg, int sz) { 
#ifdef USE_MKLvsExp(sz, inimg, outimg);
#elsefor (int i = 0; i < sz; ++i) { outimg[i] = std::expf(inimg[i]);}
#endif}void mean(const float* inimg, float* outimg, int outsz) { for (int i = 0; i < outsz; ++i) { outimg[i] = (inimg[i * 3] + inimg[i * 3 + 1] + inimg[i * 3 + 2]) / 3;}}// helpers for image indicesclass reflshadidx { public:reflshadidx(int w, int h):m_w(w), m_h(h){ }int reflidx(int x, int y) const { return m_w * y + x;}int shadidx(int x, int y) const { return m_w * m_h + reflidx(x, y);}private:int m_w;int m_h;};class imwrap { public:imwrap(const float* img, int w, int h):m_w(w), m_h(h), m_img(img){  }float operator()(int x, int y) const { assert(x >= 0);assert(y >= 0);assert(x < m_w);assert(y < m_h);return m_img[m_w * y + x];}private:const float* m_img;int m_w;int m_h;};// forward declaration of internal functionsvoid reflect_clamp(int w, int h, float* refl_in, float* shading_in);void preprocess(int w, int h, const float* img, float* logimg);void postprocess(int w, int h, float* refl_in, float* shading_in, float* refl_out, float* shading_out);Eigen::VectorXf makeB(float threshold, const float* im, int w, int h);using Triplet = Eigen::Triplet<float>;int nconstraints(int w, int h) { return w*h + 2 * w*(h - 1) + 2 * (w - 1) * h;}int nentries(int w, int h) { return 2 * (w*h + 2 * w*(h - 1) + 2 * (w - 1) * h);}std::vector<Triplet> makeTriplets(int w, int h) { reflshadidx I(w, h);printf("Assemble matrix.
");double assemble_start = get_s();std::vector <Triplet> entries;int cit = 0;for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { if (x < w - 1) { // dxR(r, c) = -lR(r, c) + lR(r, c + 1)entries.push_back(Triplet(cit, I.reflidx(x, y), -1));entries.push_back(Triplet(cit, I.reflidx(x + 1, y), +1));cit++;// dxS(r, c) = -lS(r, c) + lS(r, c + 1)entries.push_back(Triplet(cit, I.shadidx(x, y), -1));entries.push_back(Triplet(cit, I.shadidx(x + 1, y), +1));cit++;}if (y < h - 1) { entries.push_back(Triplet(cit, I.reflidx(x, y
                

更多相关:

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • union { float data[4]; struct { float x; float y; float z; }; };...

  • 在立方体贴图空间内发射光线(视线),计算球面光线(视线)会击中哪个面的哪个像素的像素值,最终生成Equirectangular全景图。 InitSceneTexture():先获取Cubemaps并将其绑定在GPU中 void InitSceneTexture() {char* path = "./Cubemaps";myEnvi...

  • 对于同一场景的2D全景图,如果想改变其视野中心位置,比如下图,初始情况下视野的中心位置是蓝框,如果想让红框的灯位于中心位置该怎么做呢? #include "opencv2/highgui/highgui.hpp" #include "opencv2/opencv.hpp" #include

  • 常量内存是NVIDIA提供的一个64KB大小的内存空间,它的处理方式和普通的全局内存和共享内存都不一样,是有cuda专门提供的。 线程束的概念:线程束是指一个包含32个线程的集合,在程序中的每一行,线程束中的每个线程都将在不同的数据上执行相同的指令。 因此,常量内存的作用是,能够将单次内存的读取操作广播到每个半线程束(即16个线程...

  •         1、初始化,设置背景色          void glClear(int mask)    清除缓存          实參含义:GL10.GL_COLOR_BUFFER_BIT 清除颜色缓存                        GL10.GL_DEPTH_BUFFER_BIT  清除深度缓存      ...