RGB归一化及高效实现
RGB归一化及高效实现微信公众号:幼儿园的学霸介绍在图像处理中,RGB颜色模型是比较常用的格式,但是其存在一个明显的缺点:容易受到光照变化或阴影的影响,也就是说,同一位置,不同光照强度会导致图像的RGB值发生很大变化.而通过对图像的RGB色彩空间进行归一化处理,在某些情况下是去除光照和阴影影响的一种简单和有效的方法.假设图像上某点的的像素值为RGB,rgb表示归一化之后的值,那么RGB归一化的公式
RGB归一化及高效实现
微信公众号:幼儿园的学霸
介绍
在图像处理中,RGB颜色模型是比较常用的格式,但是其存在一个明显的缺点:容易受到光照变化或阴影的影响,也就是说,同一位置,不同光照强度会导致图像的RGB值发生很大变化.
而通过对图像的RGB色彩空间进行归一化处理,在某些情况下是去除光照和阴影影响的一种简单和有效的方法.
假设图像上某点的的像素值为RGB,rgb表示归一化之后的值,那么RGB归一化的公式表示如下:
r = R R + G + B g = G R + G + B b = B R + G + B ( o r b = 1 − r − g ) r=\frac{R}{R+G+B} \\ g=\frac{G}{R+G+B} \\ b=\frac{B}{R+G+B} (or\ b=1-r-g) r=R+G+BRg=R+G+BGb=R+G+BB(or b=1−r−g)
从公式可以看到:
1.归一化之后的图像仅使用两个字节便可以表示一个像素值
2.当某个像素受光照或阴影的影响而产生颜色通道R、G、B上的scale变化的话,则通过归一化操作,可以消除这样的影响.
RGB归一化如何能消除光照影响?
举个例子:
T1时刻的像素A的像素值为:RGB(30, 60, 90),归一化后的值为(1/6,1/3,2/3)
T2时刻的像素A的像素值为:RGB(60, 120, 180)(受光照影响,R/G/B三个颜色通道的value产生了变化),归一化后的值为(1/6,1/3,2/3)
显然,T1时刻和T2时刻,像素A的归一化RGB值没有发生变化,可以看到,归一化RGB的好处在于,
当某个像素受光照或阴影的影响而产生颜色通道RGB上的scale变化的话,则通过归一化操作,可以消除这样的影响.
插一个概念,图像归一化就是通过一系列变换(即利用图像的不变矩寻找一组参数使其能够消除其他变换函数对图像变换的影响),
将待处理的原始图像转换成相应的唯一标准形式(该标准形式图像对平移、旋转、缩放等仿射变换具有不变特性).
类推,我们可以将光照或者阴影看做一个变换函数,该函数和图像函数相作用,那么RGB归一化的目的即是为了消除变换函数对原本函数的影响.
物理上,这种变换方式从图像上移除了光照的信息.
说明:
1.归一化RGB对于灰色(R=G=B)或黑色像素存在问题
原因:当某个像素的R=G=B时,如果由于光照变化影响其R/G/B三个通道值分别发生了变化,但是变化后值仍然为:R’ = G’ = B’,那么对它们的归一化是不起作用的(由归一化公式可知).
2.归一化RGB并不能去除所有类型的光照或阴影产生的影响
原因:由归一化RGB的公式可知,其只对R/G/B三个通道值发生scale变化(即 scale = R’/R = G’/G = B’/B)的情况时具有光照不变性.
3.归一化后,阴影区域蓝色分量偏高
原因:Phong光照模型,阴影区域的三个通道中,R通道下降最多,G通道次之,B通道下降最少,这相当于增加了阴影区域的蓝色分量
代码实现及阴影去除效果
普通实现
根据RGB归一化公式,可以很轻松实现对一幅图像进行RGB归一化的代码:直接遍历各个像素点,然后对各像素点进行求值即可.代码如下:
//
// Created by liheng on 11/13/20.
//
#include <opencv2/opencv.hpp>
#include <vector>
//RGB归一化
//Input Param:_src--输入图像,CV_8UC3
//Output Param:dst--归一化后的图像,type as src
//Return: null
void normalizeRGB1(const cv::Mat& _src,cv::Mat& dst)
{
assert( CV_8UC3==_src.type() );
cv::Mat src;
_src.convertTo(src,CV_32FC3);
for(auto iter=src.begin<cv::Vec3f>(); iter != src.end<cv::Vec3f>(); ++iter)
{
float denominator = 1.0f /((*iter)[0]+(*iter)[1]+(*iter)[2]+FLT_EPSILON);
(*iter)[0] = (*iter)[0]*denominator;
(*iter)[1] = (*iter)[1]*denominator;
(*iter)[2] = (*iter)[2]*denominator;
}
//将结果量化到[0,255]范围
//NORM_MINMAX表示线性量化,CV_8UC3表示将图像转回
cv::normalize(src,src,0,255,cv::NORM_MINMAX);
cv::convertScaleAbs(src,dst);
}
上述代码采用迭代器对各像素进行遍历,注意进行归一化操作时出现分母为0的情况(R=G=B=0),因此分母额外加了一个微小的量.
高效实现
上面的实现过程采用遍历实现,可能会比较耗时.思考RGB归一化公式,我们可以借助于矩阵运算公式,通过矩阵来实现上述过程,
借助与OpenCV对矩阵运算的优化,来加速上述过程,实现代码如下:
//RGB归一化
//Input Param:_src--输入图像,CV_8UC3
//Output Param:dst--归一化后的图像,type as src
//Return: null
void normalizeRGB(const cv::Mat &_src, cv::Mat &dst)
{
assert( CV_8UC3==_src.type() );
cv::Mat src;
_src.convertTo(src,CV_32FC3);
//拆分通道
std::vector<cv::Mat> channels;
cv::split(src,channels);
//各通道归一化
cv::Mat denominator = 1.0/(channels.at(0)+channels.at(1)+channels.at(2)+FLT_EPSILON);
//对应元素点乘
channels.at(0) = channels.at(0).mul(denominator);
channels.at(1) = channels.at(1).mul(denominator);
channels.at(2) = channels.at(2).mul(denominator);
//合并通道
cv::merge(channels,src);
//将结果量化到[0,255]范围
//NORM_MINMAX表示线性量化,CV_8UC3表示将图像转回
cv::normalize(src,src,0,255,cv::NORM_MINMAX);
cv::convertScaleAbs(src,dst);
}
效果预览
此处有一张含有阴影的图像,如下图所示.采用上面2种方式进行RGB归一化,并观察阴影去除效果:
代码示例:
#include <chrono>
int main()
{
cv::Mat frame = cv::imread("./shadow_pic.png",cv::IMREAD_COLOR);
cv::Mat dst;
cv::Mat dst2;
auto s_time1 = std::chrono::high_resolution_clock::now();
for(int i=0; i<200; ++i)
normalizeRGB(frame,dst);
auto s_time2 = std::chrono::high_resolution_clock::now();
for(int i=0; i<200;++i)
normalizeRGB1(frame,dst2);
auto s_time3 = std::chrono::high_resolution_clock::now();
auto duration1 = std::chrono::duration_cast<std::chrono::milliseconds>(s_time2-s_time1).count();
auto duration2 = std::chrono::duration_cast<std::chrono::milliseconds>(s_time3-s_time2).count();
printf("methord1 avgtime:%ld ms methord2 avgtime:%ld ms\n",duration1/200,duration2/200);
//DEBUG:methord1 avgtime:2 ms methord2 avgtime:16 ms
//RELEASE:methord1 avgtime:4 ms methord2 avgtime:6 ms
cv::hconcat(frame,dst,dst);
cv::hconcat(frame,dst2,dst2);
cv::imshow("res",dst);
cv::imshow("res2",dst2);
cv::waitKey(0);
return 0;
}
耗时分析:耗时情况在代码中已注明.在Release模式下,两种方式差别不明显.
RGB归一化结果如下(2种实现下结果一致):
参考资料
1.基于归一化RGB色彩模型的阴影处理方法,杨俊,赵忠明
2.opencv RGB归一化
3.RGB颜色模型归一化
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)