在QT框架下进行仿照PS图像调整功能设计(亮度对比度,色相饱和度及明度)

2 篇文章 0 订阅

原文链接:https://blog.csdn.net/maozefa/article/details/4778934

 图像亮度调整分为非线性和线性两种方法。

    非线性图像亮度是将图像像素的R、G、B分别加上或减去某个值,其优点是代码简单,亮度调整速度快;缺点是图像信息损失较大,调整过的 图像显得平淡,无层次感。

    线性图像亮度一般是将图像像素的RGB转换为HSL(HSV)等颜色空间,对L(V)部分进行增减调整后,再转换为RGB颜色空间,优点是调整过图像层次感很强;缺点是代码较复杂,调整速度慢,而且当图像亮度增减量较大时有很大的失真。

    针对上面两种方法的优缺点,本人参照Photoshop的对比度、饱和度调整原理(可参见本人的有关文章),对图像亮度调整方法进行了改进,经测试,效果还不错,主要有不失真调整范围宽、有较好的层次感、尽可能减少图像信息损失量等;同时,在代码处理上,采用了灰度表查找法,先按制造了一个256个元素大小的线性亮度/对比度查找表,然后对图像数据逐像素按R、G、B分量值在查找表中取得调整后的数据,因此处理速度同《Delphi图像处理 -- 亮度/对比度调整》中的非线性亮度/对比度是基本相同的。

    原理用公式表示为:

    如果亮度增减量value范围为 -1  --  +1,当value > 0时:

      rgb = RGB + RGB * (1 / (1 - value) - 1)

    当value < 0时:

      rgb = RGB + RGB * value

PS所用的正是线性方法,在PS6中也没有改变。原文代码非C++,且所用方法为HASH查表加速方法,比较难懂,我用C++实现了相同的方法没有进行HASH查表法,速度上较差。

其中色相饱和度和明度原本只以为是简单的HSV模型进行的应用,但是实际用简单加减,色相和饱和度大致相同。明度方面有较大差异,明度明显范围和PS6新版本方法不一致,在最大值处取不到白色,最小值处取不到黑色。

因此参考了原文链接:https://blog.csdn.net/matrix_space/article/details/72303250中关于明度的计算方法。

主要思想是PS的明度调整是采用Alpha合成方式,这里的value就是Alpha,公式前面部分RGB * (1 - value)的是图像部分,后面的255 * value部分则是一个白色遮照层,明度越大,遮照层的Alpha越大,图像就越谈,反之亦然。而明度的负调整则是以一个黑色遮照层来完成的。负100%就全黑了。只有遮照层Alpha=0,也就是明度值为0时,才是完完全全的图片显示。
明度调整,利用图层的合成
如果alpha大于0,相当于利用一个白色遮罩层合成
RGB = RGB * (1 - alpha) + 255 * alpha;
如果alpha小于0,相当于利用一个黑色遮罩层合成
RGB=RGB * (1+alpha) + 0 * alpha;

具体的代码实现如下范围阈值输入为

  输入参数依次为:
      i_light亮度(-150到+150)
      contrast对比度(-50到100)
      hue色相(-180到+180)
      i_saturation饱和度(-100到+100) 
      输入图像与输出图像等大小三通道

代码基于QT界面

void ImageTools::AdjustImageColor(QImage *io_image, int i_light, int i_contrast,int i_hue, int i_saturation, int i_value,
	int progress_start, int progress_end, ProgressCallBackPtr callProgress)
{
    auto myClamp = [](int a, int min, int max)->int { return (a < min) ? min : (a > max) ? max : a; };//lambda表达式

	int width = io_image->width();
	int height = io_image->height();

	int oldDisplayPos = progress_start;//界面显示进度
	int progress_total = progress_end - progress_start;//进度总量
	int total = width * height;//工作总量
	int pos = 0;//当前工作进度
    float c = i_contrast / 100.0f;
    float b = i_light/255.f;

    int nRed, nGreen, nBlue;
    int result_color[3] = {0};
    int h, s, v;
    for (int x = 0; x < width; ++x) {
        for (int y = 0; y < height; ++y) {
            QColor var = io_image->pixelColor(x, y);
            var.getHsv(&h, &s, &v);
			h += i_hue;
			if (h<0)
			{
				h = h + 360;
			}
			else if(h>360)
			{
				h = h - 360;
			}

			s += 2.55*i_saturation;
			if (s<0)
			{
				s = 0;
			}
			else if(s>255)
			{
				s = 255;
			}
            var.setHsv(h, s, v);
            var.getRgb(&nRed, &nGreen, &nBlue);

			//明度新公式
			int value= 2.55*i_value;
			if (value>0)
			{
				nRed = (nRed * (255 - value) + 255 * value) / 255;
				nGreen = (nGreen * (255 - value) + 255 * value) / 255;
				nBlue = (nBlue * (255 - value) + 255 * value) / 255;
			}
			else 
			{
				nRed = (nRed * (255 + value) + 0 * value) / 255;
				nGreen = (nGreen * (255 + value) + 0 * value) / 255;
				nBlue = (nBlue * (255+ value) + 0 * value) / 255;
			}
            int color[3] = {nRed, nGreen, nBlue};
            for (int i = 0; i < 3; ++i) {
				if (b>0.0)
				{
					color[i] = color[i] + color[i] * (1 / (1 - b) - 1)*0.85;
				}
				else
				{
					color[i] = color[i] + color[i] * b*0.65;
				}
				result_color[i] = color[i] +(color[i] -127.5) * c;
                result_color[i] = myClamp(result_color[i], 0, 255);

            }	
		
            io_image->setPixelColor(x, y, QColor(result_color[0], result_color[1], result_color[2]));

			if (callProgress) {
				//进度条设置
				pos++;
				int newDisplayPos = (double)pos / (double)total * progress_total + progress_start;
				if (newDisplayPos > oldDisplayPos && newDisplayPos <= progress_end)
				{
					oldDisplayPos = newDisplayPos;
					callProgress(oldDisplayPos);
				}
			}
        }
    }

}

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值