RGBA格式影象中HSBC(色相、飽和度、明度、對比度)調整
阿新 • • 發佈:2019-02-08
ColorMatrix.h#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 denormalized m_brightnessMatrix->SetBrightnessMatrix(value); } } void AdjustColor::setcontrast(float value) { // denormalized contrast value float 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 value float 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 denormalized if(m_hueMatrix == NULL) { m_hueMatrix = new ColorMatrix(); } if(value != 0) { // Convert to radian m_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.cpp#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
#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 left if(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 dimension Destroy(); Create(inWidth, m_height); // assign result back to self for(i = 0; i < inHeight; i++) for(j = 0; j < m_width; j++) m_matrix[i][j] = result.GetValue(i, j); } else { // inMatrix on the right if(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 dimension Destroy(); Create(m_width, inHeight); // assign result back to self for(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 SVG float 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 computed sinBaseMat.SetValue(2, 0, -(1 - lumR)); sinBaseMat.SetValue(0, 1, -lumG); sinBaseMat.SetValue(1, 1, 0.140); // not sure how this value is computed sinBaseMat.SetValue(2, 1, lumG); sinBaseMat.SetValue(0, 2, (1 - lumB)); sinBaseMat.SetValue(1, 2, -0.283); // not sure how this value is computed sinBaseMat.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的同樣的值調整出來的不一樣,可能是經驗值導致的,具體原因還不清楚。如果需要可以做一個工具讓美術根據這個演算法調整相應值。