RGBA格式图像中HSBC(色相、饱和度、明度、对比度)调整

来源:互联网 发布:苹果手机视频软件 编辑:程序博客网 时间:2024/05/20 20:05

最近项目中有一个需求:

美术给定一张图片,通过配置文件,在不同建筑等级时使用同一张图片,但是在不同等级时通过程序不断调整hsbc(photoshop中也可以调整)显示出不同的效果。通过搜索,有两种方法解决:


首先介绍下HSL和HSV、HSB:

HSL 和 HSV(也叫HSB)是对RGB 色彩空间中点的两种有关系的表示,它们尝试描述比 RGB 更准确的感知颜色联系,并仍保持在计算上简单。

H指hue(色相)、S指saturation(饱和度)、L指lightness(亮度)、V指value(色调)、B指brightness(明度)

  • 色相(H)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。
  • 饱和度(S)是指色彩的纯度,越高色彩越纯,低则逐渐变灰,取0-100%的数值。
  • 明度(V),亮度(B),取0-100%。

方法一:
因此要调整RGBA格式的的hsbc,可以先将像素的RGB值转换成HSB格式,然后调整HSB再转换成RGB值就可以了,本人用这种方法处理一张1024*768的图片在pc上执行是时间大约是200ms,在每秒30帧的游戏中这样的速度显然是不行的,如果不考虑效率,这种方法也是非常方便的,建议不用担心效率的时候使用该方法。

方法二:
有没有不用转换格式直接对RGB做特殊处理呢,答案是肯定的:参照AS中ColorMatrixFilter色彩矩阵滤镜
参考链接:http://www.comingx.com/?p=1088

这里的matrix其实是一个Array;

matrix:Array  [read-write]

由 20 个项目组成的数组,适用于 4 x 5 颜色转换。

颜色矩阵滤镜将每个源像素分离成它的红色、绿色、蓝色和 Alpha 成分,分别以 srcR、srcG、srcB 和 srcA 表示。 若要计算四个通道中每个通道的结果,可将图像中每个像素的值乘以转换矩阵中的值。 (可选)可以将偏移量(介于 -255 至 255 之间)添加到每个结果(矩阵的每行中的第五项)中。 滤镜将各颜色成分重新组合为单一像素,并写出结果。 在下列公式中,a[0] 到 a[19] 对应于由 20 个项目组成的数组中的条目 0 至 19,该数组已传递到 matrix 属性:

redResult = (a[0] * srcR) + (a[1] * srcG) + (a[2] * srcB) + (a[3] * srcA) + a[4]

greenResult = (a[5] * srcR) + (a[6] * srcG) + (a[7] * srcB) + (a[8] * srcA) + a[9]

blueResult = (a[10] * srcR) + (a[11] * srcG) + (a[12] * srcB) + (a[13] * srcA) + a[14]

alphaResult = (a[15] * srcR) + (a[16] * srcG) + (a[17] * srcB) + (a[18] * srcA) + a[19]
对于数组中的每个颜色值,值 1 等于正发送到输出的通道的 100%,同时保留颜色通道的值。

所以整个过程中最重要的是求出这个a数组,请参考以下C++的实现,稍微看得懂C++的就可以将其转换成C代码或者其他语言的~

类说明
AdjustColor:传入色相、饱和度、明度、对比度值,得出结果的类
ColorMatrix:根据色相、饱和度、明度、对比度值的值分别调整数组内容的类

调用方式可以参考AdjustColor.h中的C接口void ColorAdjust(ColorHSB_T& hsb, void* SrcData, int width, int height)

下面是用到的几个文件:
AdjustColor.h
#ifndef ADJUSTCOLOR_H_#define ADJUSTCOLOR_H_#include "ColorMatrix.h"struct ColorHSB_T{int hue;int saturation;int brightness;int contrast;ColorHSB_T(int h = 0, int s = 0, int b = 0, int c = 0):hue(h),saturation(s),brightness(b),contrast(c){}};#define PI (3.1415926535)class AdjustColor{public:AdjustColor();~AdjustColor();void setbrightness(float value);void setcontrast(float value);void setsaturation(float value);void sethue(float value);float** CalculateFinalFlatArray();bool AllValuesAreSet();bool CalculateFinalMatrix();protected:private:static float s_arrayOfDeltaIndex[];ColorMatrix* m_brightnessMatrix;ColorMatrix* m_contrastMatrix;ColorMatrix* m_saturationMatrix;ColorMatrix* m_hueMatrix;ColorMatrix* m_finalMatrix;};void ColorAdjust(ColorHSB_T& hsb, void* SrcData, int width, int height);#endif

AdjustColor.cpp
#include "AdjustColor.h"#include <math.h>#include <stdlib.h>#include <stdio.h>float** s_arr = NULL;float AdjustColor::s_arrayOfDeltaIndex[]= {//      0     1     2     3     4     5     6     7     8     9                                        /*0*/   0,    0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1,  0.11,/*1*/   0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,/*2*/   0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,/*3*/   0.44, 0.46, 0.48, 0.5,  0.53, 0.56, 0.59, 0.62, 0.65, 0.68, /*4*/   0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,/*5*/   1.0,  1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,/*6*/   1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0,  2.12, 2.25, /*7*/   2.37, 2.50, 2.62, 2.75, 2.87, 3.0,  3.2,  3.4,  3.6,  3.8,/*8*/   4.0,  4.3,  4.7,  4.9,  5.0,  5.5,  6.0,  6.5,  6.8,  7.0,/*9*/   7.3,  7.5,  7.8,  8.0,  8.4,  8.7,  9.0,  9.4,  9.6,  9.8, /*10*/  10.0  };AdjustColor::AdjustColor():m_brightnessMatrix(NULL),m_contrastMatrix(NULL),m_saturationMatrix(NULL),m_hueMatrix(NULL),m_finalMatrix(NULL){}AdjustColor::~AdjustColor(){if (m_brightnessMatrix){delete m_brightnessMatrix;}if(m_contrastMatrix){delete m_contrastMatrix;}if (m_saturationMatrix){delete m_saturationMatrix;}if (m_finalMatrix){delete m_finalMatrix;}}// AdjustColor* AdjustColor::GetInstance()// {// if (s_adjust)// {// s_adjust = new AdjustColor;// }// return s_adjust;// }void AdjustColor::setbrightness(float value){if(m_brightnessMatrix == NULL){m_brightnessMatrix = new ColorMatrix();}if(value != 0){// brightness does not need to be denormalizedm_brightnessMatrix->SetBrightnessMatrix(value);}}void AdjustColor::setcontrast(float value){// denormalized contrast valuefloat deNormVal = value;if(value == 0) {deNormVal = 127;}else if(value > 0) {deNormVal = s_arrayOfDeltaIndex[int(value)] * 127 + 127;}else {deNormVal = (value / 100 * 127) + 127;}if(m_contrastMatrix == NULL){m_contrastMatrix = new ColorMatrix();}m_contrastMatrix->SetContrastMatrix(deNormVal);}void AdjustColor::setsaturation(float value){// denormalized saturation valuefloat deNormVal = value;if (value == 0) {deNormVal = 1;} else if (value > 0) {deNormVal = 1.0 + (3 * value / 100); // max value is 4} else {deNormVal = value / 100 + 1;}if(m_saturationMatrix == NULL){m_saturationMatrix = new ColorMatrix();}m_saturationMatrix->SetSaturationMatrix(deNormVal);}void AdjustColor::sethue(float value){// hue value does not need to be denormalizedif(m_hueMatrix == NULL){m_hueMatrix = new ColorMatrix();}if(value != 0){// Convert to radianm_hueMatrix->SetHueMatrix(value * PI / 180.0);}}bool AdjustColor::AllValuesAreSet(){return (m_brightnessMatrix && m_contrastMatrix && m_saturationMatrix && m_hueMatrix);}float** AdjustColor::CalculateFinalFlatArray(){if(CalculateFinalMatrix()){return m_finalMatrix->GetFlatArray();}return NULL;}bool AdjustColor::CalculateFinalMatrix(){if(!AllValuesAreSet()) return false;if (!m_finalMatrix){m_finalMatrix = new ColorMatrix();}m_finalMatrix->Multiply(*m_brightnessMatrix);m_finalMatrix->Multiply(*m_contrastMatrix);m_finalMatrix->Multiply(*m_saturationMatrix);m_finalMatrix->Multiply(*m_hueMatrix);return true;}void ColorAdjust(int brightness, int contrast, int saturation, int hue, void* SrcData, int width, int height){if (NULL == SrcData){return;}printf("ColorAdjustColorAdjustColorAdjustColorAdjustColorAdjustColorAdjustColorAdjust!!!\n");AdjustColor  s_adjustColor;s_adjustColor.setbrightness(brightness);s_adjustColor.setcontrast(contrast);s_adjustColor.setsaturation(saturation);s_adjustColor.sethue(hue);float** arr = s_adjustColor.CalculateFinalFlatArray();unsigned char* pData = (unsigned char*)SrcData;for (long i =0; i < width * height * 4; i+=4){int R,G,B,A;R=pData[i];G=pData[i+1];B=pData[i+2];A=pData[i+3];if (0 == A){continue;}for(int j = 0; j < 3; j++){int res = arr[j][0]*R + arr[j][1]*G + arr[j][2]*B +arr[j][3]*A + arr[j][4];if (res < 0){res = 0;}if (res > 255){res = 255;}pData[i + j] = res;}}}AdjustColor  s_adjustColor;void SetColorAdjust(int brightness, int contrast, int saturation, int hue){s_adjustColor.setbrightness(brightness);s_adjustColor.setcontrast(contrast);s_adjustColor.setsaturation(saturation);s_adjustColor.sethue(hue);s_arr = s_adjustColor.CalculateFinalFlatArray();}float GetResult(int row, int col){float val = 0;if (s_arr && row < 5 && col < 5){val = s_arr[row][col];}return val;}

ColorMatrix.h
#ifndef COLORMATRIE_H_#define COLORMATRIE_H_#include <stdlib.h>#if defined COLORMATRIX_DLL_EXPORT#define COLORMATRIX_DECLDIR __declspec(dllexport)#else#define COLORMATRIX_DECLDIR __declspec(dllimport)#endif class COLORMATRIX_DECLDIR DynamicMatrix{public:static const int MATRIX_ORDER_PREPEND = 0;static const int MATRIX_ORDER_APPEND = 1;DynamicMatrix(int w, int h);~DynamicMatrix();int GetWidth();int GetHeight();float GetValue(int row, int col);void SetValue(int row, int col, float value);void LoadIdentity();void LoadZeros();bool Multiply(DynamicMatrix& inMatrix, int order = MATRIX_ORDER_PREPEND);bool MultiplyNumber(float value);bool Add(DynamicMatrix&inMatrix);protected:void Create(int width, int height);void Destroy();int m_width;int m_height;float** m_matrix;};class ColorMatrix:public DynamicMatrix{public:ColorMatrix();void SetBrightnessMatrix(float value);void SetContrastMatrix(float value);void SetSaturationMatrix(float value);void SetHueMatrix(float angle);float** GetFlatArray();protected:static float LUMINANCER;static float LUMINANCEG;static float LUMINANCEB;};#endif

ColorMatrix.cpp
#include "ColorMatrix.h"#include <math.h>int max(int a, int b){return a > b ? a:b;}DynamicMatrix::DynamicMatrix(int w, int h){m_width = 0;m_height = 0;m_matrix = NULL;Create(w, h);}void DynamicMatrix::Create(int width, int height){if(width <= 0 || height <= 0) return;m_width = width;m_height = height;m_matrix = new float*[height];for(int i = 0; i < height; i++){m_matrix[i] = new float[width];for(int j = 0; j < height; j++){m_matrix[i][j] = 0;}}}DynamicMatrix::~DynamicMatrix(){Destroy();}void DynamicMatrix::Destroy(){if (m_matrix){for(int i = 0; i < m_height; i++){delete [](m_matrix[i]);}m_matrix = NULL;}}int DynamicMatrix::GetWidth(){return m_width;}int DynamicMatrix::GetHeight(){return m_height;}float DynamicMatrix::GetValue(int row, int col){float val = 0;if (row >= 0 && row < m_height && col >= 0 && col <= m_width){val = m_matrix[row][col];}return val;}void DynamicMatrix::SetValue(int row, int col, float value){if(row >= 0 && row < m_height && col >= 0 && col <= m_width){m_matrix[row][col] = value;}}void DynamicMatrix::LoadIdentity(){if(NULL == m_matrix) {return;}for(int i = 0; i < m_height; i++)for(int j = 0; j < m_width; j++){if(i == j) {m_matrix[i][j] = 1;}else {m_matrix[i][j] = 0;}}}void DynamicMatrix::LoadZeros(){if(NULL == m_matrix){return;}for(int i = 0; i < m_height; i++)for(int j = 0; j < m_width; j++)m_matrix[i][j] = 0;}bool DynamicMatrix::Multiply(DynamicMatrix& inMatrix, int order){if(!m_matrix)return false;int inHeight = inMatrix.GetHeight();int inWidth = inMatrix.GetWidth();int i = 0;int j = 0;int k = 0;int m = 0;float total = 0;DynamicMatrix* result = NULL;if(order == MATRIX_ORDER_APPEND){//inMatrix on the leftif(m_width != inHeight)return false;DynamicMatrix result(inWidth, m_height);for(i = 0; i < m_height; i++)for(j = 0; j < inWidth; j++){total = 0;for(k = 0, m = 0; k < max(m_height, inHeight) && m < max(m_width, inWidth); k++, m++){total = total + (inMatrix.GetValue(k, j) * m_matrix[i][m]);}result.SetValue(i, j, total);}// destroy self and recreate with a new dimensionDestroy();Create(inWidth, m_height);// assign result back to selffor(i = 0; i < inHeight; i++)for(j = 0; j < m_width; j++) m_matrix[i][j] = result.GetValue(i, j);}else {// inMatrix on the rightif(m_height != inWidth)return false;DynamicMatrix result(m_width, inHeight);for(i = 0; i < inHeight; i++)for(j = 0; j < m_width; j++){total = 0;for(k = 0, m = 0; k < max(inHeight, m_height) && m < max(inWidth, m_width); k++, m++){total = total + ( (m_matrix[k][j]) * (inMatrix.GetValue(i, m)));}result.SetValue(i, j, total);}// destroy self and recreate with a new dimensionDestroy();Create(m_width, inHeight);// assign result back to selffor(i = 0; i < inHeight; i++)for(j = 0; j < m_width; j++)m_matrix[i][j] = result.GetValue(i, j);}return true;}bool DynamicMatrix::MultiplyNumber(float value){if(!m_matrix)return false;for(int i = 0; i < m_height; i++)for(int j = 0; j < m_width; j++){float total = 0;total = m_matrix[i][j] * value;m_matrix[i][j] = total;}return true;}bool DynamicMatrix::Add(DynamicMatrix&inMatrix){if(!m_matrix)return false;int inHeight = inMatrix.GetHeight();int inWidth = inMatrix.GetWidth();if(m_width != inWidth || m_height != inHeight)return false;for(int i = 0; i < m_height; i++)for(int j = 0; j < m_width; j++){float total = 0;total = m_matrix[i][j] + inMatrix.GetValue(i, j);m_matrix[i][j] = total;}return true;}///////////////////////////////////////////////////float ColorMatrix::LUMINANCER = 0.3086;float ColorMatrix::LUMINANCEG = 0.6094;float ColorMatrix::LUMINANCEB = 0.0820;ColorMatrix::ColorMatrix():DynamicMatrix(5, 5){LoadIdentity();}void ColorMatrix::SetBrightnessMatrix(float value){if (!m_matrix)return;m_matrix[0][4] = value;m_matrix[1][4] = value;m_matrix[2][4] = value;}void ColorMatrix::SetContrastMatrix(float value){if(!m_matrix)return;float brightness= 0.5 * (127.0 - value);value = value / 127.0;m_matrix[0][0] = value;m_matrix[1][1] = value;m_matrix[2][2] = value;m_matrix[0][4] = brightness;m_matrix[1][4] = brightness;m_matrix[2][4] = brightness;}void ColorMatrix::SetSaturationMatrix(float value){if(!m_matrix)return;float subVal = 1.0 - value;float mulVal= subVal * LUMINANCER;m_matrix[0][0] = mulVal + value;m_matrix[1][0] = mulVal;m_matrix[2][0] = mulVal;mulVal = subVal * LUMINANCEG;m_matrix[0][1] = mulVal;m_matrix[1][1] = mulVal + value;m_matrix[2][1] = mulVal;mulVal = subVal * LUMINANCEB;m_matrix[0][2] = mulVal;m_matrix[1][2] = mulVal;m_matrix[2][2] = mulVal + value;}void ColorMatrix::SetHueMatrix(float angle){if(!m_matrix)return;LoadIdentity();DynamicMatrix baseMat(3, 3);DynamicMatrix cosBaseMat(3, 3);DynamicMatrix sinBaseMat(3, 3);float cosValue = cos(angle);float sinValue = sin(angle);// slightly smaller luminance values from SVGfloat lumR = 0.213;float lumG = 0.715;float lumB = 0.072;baseMat.SetValue(0, 0, lumR);baseMat.SetValue(1, 0, lumR);baseMat.SetValue(2, 0, lumR);baseMat.SetValue(0, 1, lumG);baseMat.SetValue(1, 1, lumG);baseMat.SetValue(2, 1, lumG);baseMat.SetValue(0, 2, lumB);baseMat.SetValue(1, 2, lumB);baseMat.SetValue(2, 2, lumB);cosBaseMat.SetValue(0, 0, (1 - lumR));cosBaseMat.SetValue(1, 0, -lumR);cosBaseMat.SetValue(2, 0, -lumR);cosBaseMat.SetValue(0, 1, -lumG);cosBaseMat.SetValue(1, 1, (1 - lumG));cosBaseMat.SetValue(2, 1, -lumG);cosBaseMat.SetValue(0, 2, -lumB);cosBaseMat.SetValue(1, 2, -lumB);cosBaseMat.SetValue(2, 2, (1 - lumB));cosBaseMat.MultiplyNumber(cosValue);sinBaseMat.SetValue(0, 0, -lumR);sinBaseMat.SetValue(1, 0, 0.143);// not sure how this value is computedsinBaseMat.SetValue(2, 0, -(1 - lumR));sinBaseMat.SetValue(0, 1, -lumG);sinBaseMat.SetValue(1, 1, 0.140);// not sure how this value is computedsinBaseMat.SetValue(2, 1, lumG);sinBaseMat.SetValue(0, 2, (1 - lumB));sinBaseMat.SetValue(1, 2, -0.283);// not sure how this value is computedsinBaseMat.SetValue(2, 2, lumB);sinBaseMat.MultiplyNumber(sinValue);baseMat.Add(cosBaseMat);baseMat.Add(sinBaseMat);for(int i = 0; i < 3; i++)for(int j = 0; j < 3; j++)m_matrix[i][j] = baseMat.GetValue(i, j);}float** ColorMatrix::GetFlatArray(){if(!m_matrix)return NULL;return m_matrix;}

通过测试使用同样一张1024*768的图片,用这个方法使用的时间在40ms左右,效率相对来说比较高。但是效果和photoshop的同样的值调整出来的不一样,可能是经验值导致的,具体原因还不清楚。如果需要可以做一个工具让美术根据这个算法调整相应值。



原创粉丝点击