├── readme.txt ├── lbp ├── lbp_new.m └── getmapping.m ├── Get_LIVE_Data.m ├── Mian_LIVE_Test.m ├── Get_LIVE_Result.m ├── Utility ├── SVMcgForIQA.m ├── find_Ref_LIVE.m ├── SVMcgForRegress.m ├── estimateggdparam.m ├── feature_scaling.m ├── get_LIVE_ImagePath.m ├── SVM_SROCC_Search_cg.m ├── get_LIVE_distortion_type.m ├── SVM_SROCC_Search_cg_Cross_database.m └── estimateaggdparam.m ├── Result ├── LIVE_dist_type.mat ├── LIVE_image_path.mat ├── LIVE_color_lbp_data.mat └── LIVE_color_lbp_0_featnum_46_result_ALL.mat ├── 颜色空间统计联合纹理特征的无参考图像质量评价.pdf ├── color_feat ├── convertRGBToLMS.m ├── divisiveNormalization.m ├── lmsColorOpponentFeats.m └── colorspace.m ├── feat_extract_all.m └── README.md /readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/readme.txt -------------------------------------------------------------------------------- /lbp/lbp_new.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/lbp/lbp_new.m -------------------------------------------------------------------------------- /Get_LIVE_Data.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Get_LIVE_Data.m -------------------------------------------------------------------------------- /Mian_LIVE_Test.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Mian_LIVE_Test.m -------------------------------------------------------------------------------- /Get_LIVE_Result.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Get_LIVE_Result.m -------------------------------------------------------------------------------- /Utility/SVMcgForIQA.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/SVMcgForIQA.m -------------------------------------------------------------------------------- /Utility/find_Ref_LIVE.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/find_Ref_LIVE.m -------------------------------------------------------------------------------- /Result/LIVE_dist_type.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Result/LIVE_dist_type.mat -------------------------------------------------------------------------------- /Result/LIVE_image_path.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Result/LIVE_image_path.mat -------------------------------------------------------------------------------- /Utility/SVMcgForRegress.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/SVMcgForRegress.m -------------------------------------------------------------------------------- /Utility/estimateggdparam.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/estimateggdparam.m -------------------------------------------------------------------------------- /Utility/feature_scaling.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/feature_scaling.m -------------------------------------------------------------------------------- /颜色空间统计联合纹理特征的无参考图像质量评价.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/颜色空间统计联合纹理特征的无参考图像质量评价.pdf -------------------------------------------------------------------------------- /Utility/get_LIVE_ImagePath.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/get_LIVE_ImagePath.m -------------------------------------------------------------------------------- /Result/LIVE_color_lbp_data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Result/LIVE_color_lbp_data.mat -------------------------------------------------------------------------------- /Utility/SVM_SROCC_Search_cg.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/SVM_SROCC_Search_cg.m -------------------------------------------------------------------------------- /Utility/get_LIVE_distortion_type.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/get_LIVE_distortion_type.m -------------------------------------------------------------------------------- /Utility/SVM_SROCC_Search_cg_Cross_database.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Utility/SVM_SROCC_Search_cg_Cross_database.m -------------------------------------------------------------------------------- /Result/LIVE_color_lbp_0_featnum_46_result_ALL.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranjiewwen/IQA-paper/HEAD/Result/LIVE_color_lbp_0_featnum_46_result_ALL.mat -------------------------------------------------------------------------------- /color_feat/convertRGBToLMS.m: -------------------------------------------------------------------------------- 1 | %{ 2 | Author: 3 | 4 | Description: Given an input RGB image, this method transforms the input 5 | into LMS color space. 6 | %} 7 | 8 | function lms= convertRGBToLMS(rgb) 9 | lms = (colorspace('RGB->CAT02 LMS',rgb)); 10 | end 11 | -------------------------------------------------------------------------------- /color_feat/divisiveNormalization.m: -------------------------------------------------------------------------------- 1 | function [structdis,iMinusMu,mu,sigma]= divisiveNormalization(imdist) 2 | 3 | window = fspecial('gaussian',7,7/6); 4 | window = window/sum(sum(window)); 5 | 6 | mu = filter2(window, imdist, 'same'); 7 | mu_sq = mu.*mu; 8 | 9 | sigma = sqrt(abs(filter2(window, imdist.*imdist, 'same') - mu_sq)); 10 | iMinusMu = (imdist-mu); 11 | structdis =iMinusMu./(sigma +1); 12 | end -------------------------------------------------------------------------------- /Utility/estimateaggdparam.m: -------------------------------------------------------------------------------- 1 | function [alpha leftstd rightstd] = estimateaggdparam(vec) 2 | 3 | 4 | gam = 0.2:0.001:10; 5 | r_gam = ((gamma(2./gam)).^2)./(gamma(1./gam).*gamma(3./gam)); 6 | 7 | 8 | leftstd = sqrt(mean((vec(vec<0)).^2)); 9 | rightstd = sqrt(mean((vec(vec>0)).^2)); 10 | gammahat = leftstd/rightstd; 11 | rhat = (mean(abs(vec)))^2/mean((vec).^2); 12 | rhatnorm = (rhat*(gammahat^3 +1)*(gammahat+1))/((gammahat^2 +1)^2); 13 | [min_difference, array_position] = min((r_gam - rhatnorm).^2); 14 | alpha = gam(array_position); 15 | 16 | 17 | -------------------------------------------------------------------------------- /feat_extract_all.m: -------------------------------------------------------------------------------- 1 | function feat = feat_extract_all(im2color) 2 | 3 | addpath(genpath('color_feat')); 4 | addpath(genpath('lbp')); 5 | 6 | % Convert the input RGB image to LMS color space. 7 | lms = convertRGBToLMS(im2color); 8 | %figure;imshow((lms)); 9 | featOpp = lmsColorOpponentFeats(lms); % Color opponency features. 10 | 11 | %------------------------------------------------ 12 | % Feature Computation 13 | %------------------------------------------------- 14 | scalenum = 3; 15 | window = fspecial('gaussian',3,3/2); % fspecial函数用于创建预定义的滤波算子 16 | window = window/sum(sum(window)); 17 | 18 | %% LBP 19 | R = 1; P = 8; 20 | lbp_type = { 'ri' 'u2' 'riu2' }; 21 | y = 3; 22 | mtype = lbp_type{y}; 23 | MAPPING = getmapping( P, mtype ); 24 | 25 | feat = []; 26 | imdist=double(rgb2gray(im2color)); 27 | %tic %在MATLAB里面可以使用tic和toc命令得到运行时间 28 | for itr_scale = 1:scalenum 29 | %im2color = imdist; 30 | mu = filter2(window, imdist, 'same'); 31 | mu_sq = mu.*mu; 32 | sigma = sqrt(abs(filter2(window, imdist.*imdist, 'same') - mu_sq)); 33 | structdis = (imdist-mu)./(sigma+1); 34 | %figure;imshow(mat2gray(structdis)); 35 | [alpha overallstd] = estimateggdparam(structdis(:)); % GGD 36 | feat = [feat alpha overallstd]; % 37 | 38 | %%%%%%%%%% MSCN LBP %%%%%%%%%%%% 39 | LBPMap = lbp_new(structdis,R,P,MAPPING,'x'); 40 | %%%%%% gradient magnitude weighted GLBP %%%%% 41 | wLBPHist = []; 42 | weightmap = structdis; 43 | wintesity = weightmap(2:end-1, 2:end-1); 44 | wintensity = abs(wintesity); 45 | for k = 1:max(LBPMap(:))+1 46 | idx = find(LBPMap == k-1); 47 | kval = sum(wintensity(idx)); 48 | wLBPHist = [wLBPHist kval]; 49 | end 50 | wLBPHist = wLBPHist/sum(wLBPHist); 51 | feat = [feat wLBPHist]; 52 | 53 | if itr_scale==1; 54 | feat=[feat featOpp]; 55 | end 56 | 57 | imdist = imresize(imdist,0.5); 58 | 59 | end 60 | %toc 61 | -------------------------------------------------------------------------------- /color_feat/lmsColorOpponentFeats.m: -------------------------------------------------------------------------------- 1 | %{ 2 | Description: Given an input image in LMS color space, this method extracts 3 | the color opponent features described in 'LMS Feature Maps' section of [1] 4 | 5 | Input: a MXNX3 array of LMS map 6 | 7 | Output: Features extracted from the color opponent maps constructed in LMS color space(as described in the section 8 | LMS Feature Maps in [1]) 9 | 10 | Dependencies: This method depends on the following methods: 11 | divisiveNormalization.m 12 | estimateaggdparam.m 13 | 14 | Reference: 15 | [1] D. Ghadiyaram and A.C. Bovik, "Perceptual Quality Prediction on Authentically Distorted Images Using a 16 | Bag of Features Approach," http://arxiv.org/abs/1609.04757 17 | %} 18 | 19 | function feat = lmsColorOpponentFeats(lms) 20 | % Extract the three channel maps (L,M, and S) 21 | l = double(lms(:,:,1))+1; 22 | m = double(lms(:,:,2))+1; 23 | s = double(lms(:,:,3))+1; 24 | 25 | % Apply divisive normalization on log of these channel maps. 26 | dL = divisiveNormalization(log(l)); 27 | dM = divisiveNormalization(log(m)); 28 | dS = divisiveNormalization(log(s)); 29 | 30 | % Constructing the BY-color opponency map. 31 | dBY = (dL+dM-2*dS)./sqrt(6); 32 | 33 | % Extracting model parameters (derived from AGGD fit) and the sample 34 | % statistical features (kurtosis and skewness) 35 | [beta2,lsigma2,rsigma2] = estimateaggdparam(dBY(:)); 36 | kurt2=kurtosis(dBY(:)); 37 | sk2 = skewness(dBY(:)); 38 | 39 | % Constructing the RG-color opponency map. 40 | dRG = (dL-dM)./sqrt(2); 41 | 42 | % Extracting model parameters (derived from AGGD fit) and the sample 43 | % statistical features (kurtosis and skewness) 44 | [beta3,lsigma3,rsigma3] = estimateaggdparam(dRG(:)); 45 | kurt3=kurtosis(dRG(:)); 46 | sk3 = skewness(dRG(:)); 47 | 48 | % Aggregating the final set of features from both the opponency maps. 49 | modelParamsBY = [beta2,lsigma2,rsigma2]; 50 | sampleParamsBY = [kurt2 sk2]; 51 | 52 | modelParamsRG = [beta3,lsigma3,rsigma3]; 53 | sampleParamsRG = [kurt3 sk3]; 54 | 55 | feat = [modelParamsRG sampleParamsRG modelParamsBY sampleParamsBY]; 56 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IQA-paper Image Quality Assessment 2 | 3 | ## 实验方法 4 | 5 | - (1)提取整个数据库图像的特征和对应的主观得分(DMOS/MOS); 6 | - (2)将参考图像随机分为80%和20%,找到对应的失真图像,得到80%的训练集和20%的测试集,该步骤保证了训练集和测试集没有任何重复图像; 7 | - (3)利用训练集对应的特征和质量得分训练评价模型,并预测测试图像集中图像质量得分; 8 | - (4)计算测试图像集中预测得分和真实得分之间的SROCC和PLCC; 9 | - (5)重复(2),(3),(4)步骤1000次,选取1000次实验结果SROCC和PLCC的中值作为该算法最终评价指标。 10 | 11 | ## 颜色空间统计联合纹理特征的无参考图像质量评价 12 | 13 | - 文件夹说明: 14 | 15 | - color_feat:为一些颜色特征提取的辅助函数 16 | 17 | - feat_data:为待评价图像中间特征结果 18 | 19 | - lbp:为lbp特征提取函数 20 | 21 | - Result:为程序结果保存 22 | 23 | - Utility:为提取拟合函数,LIVE库一些参数提取函数(其他库类似),SVR网格搜索函数 24 | 25 | 26 | - m文件说明: 27 | 28 | - feat_extract_all:本文特征提取方法 29 | 30 | - Get_LIVE_Data:提取LIVE库的各种参数 31 | 32 | - Get_LIVE_Result:获得LIVE库上各种结果 33 | 34 | - Mian_LIVE_Test:LIVE库上主函数 35 | 36 | ## 运行方法 37 | 38 | - 在`Get_LIVE_Data.m`函数中设置LIVE数据库的路径:`Data_Dir`即可;然后运行`Mian_LIVE_Test.m` 39 | 40 | - 实验结果 41 | 42 | ``` 43 | result = 44 | 45 | info: 'LIVE--color_lbp--Order_by_name--is_srocc_search: 0' 46 | bestc: 256 47 | bestg: 0.1088 48 | libsvm_options: '-s 3 -t 2 -g 0.11 -c 256.000000' 49 | spear_median: 0.9599 50 | spear_max: 0.9823 51 | spear_min: 0.9163 52 | plcc_median: 0.9628 53 | plcc_max: 0.9823 54 | plcc_min: 0.9200 55 | cur_date: '09-Nov-2017 16:45:22' 56 | spear_median_jp2k: 0.9514 57 | plcc_median_jp2k: 0.9636 58 | spear_median_jpeg: 0.9714 59 | plcc_median_jpeg: 0.9844 60 | spear_median_wn: 0.9858 61 | plcc_median_wn: 0.9924 62 | spear_median_GB: 0.9439 63 | plcc_median_GB: 0.9570 64 | spear_median_FF: 0.9017 65 | plcc_median_FF: 0.9369 66 | 67 | ``` 68 | 69 | 70 | ## 说明 71 | 72 | - 实验过程中多谢[Image & Video Quality Assessment at LIVE ](http://live.ece.utexas.edu/research/quality/index.htm)实验室开源主页 73 | 74 | - 本项目code只包含LIVE库代码,其他CSIQ,TID2013,MLIVE库实验可以类似实现,**发现之前滤波窗口设置为`7*7`,改为`3*3`实验效果更好** 75 | 76 | - 实验结果可能有一些偏差,但不大,与SVR网格搜索寻优有关或者其他随机因素 77 | 78 | - 代码框架容易拓展,包括其他库的实现和添加新的算法 79 | 80 | - 以后努力方向:利用CNN实现特征提取和实现端到端的图像质量评价 81 | 82 | - 知网下载地址: [颜色空间统计联合纹理特征的无参考图像质量评价](http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CJFQ&dbname=CJFDTEMP&filename=GXJM201804021&uid=WEEvREcwSlJHSldRa1FhdkJkVWI3Y2ZOcnpMbVA1czFEcXJKODlNU3JMST0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4ggI8Fm4gTkoUKaID8j8gFw!!&v=MDI2NjdTN0RoMVQzcVRyV00xRnJDVVJMS2ZZdVpwRnl2a1dyclBJalhCWTdHNEg5bk1xNDlIWllSOGVYMUx1eFk=) 83 | -------------------------------------------------------------------------------- /lbp/getmapping.m: -------------------------------------------------------------------------------- 1 | %GETMAPPING returns a mapping table for LBP codes. 2 | % MAPPING = GETMAPPING(SAMPLES,MAPPINGTYPE) returns a mapping for 3 | % LBP codes in a neighbourhood of SAMPLES sampling 4 | % points. Possible values for MAPPINGTYPE are 5 | % 'u2' for uniform LBP 6 | % 'ri' for rotation-invariant LBP 7 | % 'riu2' for uniform rotation-invariant LBP. 8 | % 9 | % Example: 10 | % I=imread('rice.tif'); 11 | % MAPPING=getmapping(16,'riu2'); 12 | % LBPHIST=lbp(I,2,16,MAPPING,'hist'); 13 | % Now LBPHIST contains a rotation-invariant uniform LBP 14 | % histogram in a (16,2) neighbourhood. 15 | % 16 | 17 | function mapping = getmapping(samples,mappingtype) 18 | % Version 0.1 19 | % Authors: Marko Heikkil?and Timo Ahonen 20 | 21 | 22 | mapping = 0:2^samples-1; 23 | newMax = 0; %number of patterns in the resulting LBP code 24 | index = 0; 25 | 26 | if strcmp(mappingtype,'u2') %Uniform 2 27 | newMax = samples*(samples-1) + 3; 28 | for i = 0:2^samples-1 29 | j = bitset(bitshift(i,1,samples),1,bitget(i,samples)); %rotate left 30 | numt = sum(bitget(bitxor(i,j),1:samples)); %number of 1->0 and 31 | %0->1 transitions 32 | %in binary string 33 | %x is equal to the 34 | %number of 1-bits in 35 | %XOR(x,Rotate left(x)) 36 | if numt <= 2 37 | mapping(i+1) = index; 38 | index = index + 1; 39 | else 40 | mapping(i+1) = newMax - 1; 41 | end 42 | end 43 | end 44 | 45 | if strcmp(mappingtype,'ri') %Rotation invariant 46 | tmpMap = zeros(2^samples) - 1; 47 | for i = 0:2^samples-1 48 | rm = i; 49 | r = i; 50 | for j = 1:samples-1 51 | r = bitset(bitshift(r,1,samples),1,bitget(r,samples)); %rotate 52 | %left 53 | if r < rm 54 | rm = r; 55 | end 56 | end 57 | if tmpMap(rm+1) < 0 58 | tmpMap(rm+1) = newMax; 59 | newMax = newMax + 1; 60 | end 61 | mapping(i+1) = tmpMap(rm+1); 62 | end 63 | end 64 | 65 | if strcmp(mappingtype,'riu2') %Uniform & Rotation invariant 66 | newMax = samples + 2; 67 | for i = 0:2^samples - 1 68 | %j = bitset(bitshift(i,1,samples),1,bitget(i,samples)); %rotate left 69 | j = bitset(bitshift(i,1),1,bitget(i,samples)); %rotate left 70 | numt = sum(bitget(bitxor(i,j),1:samples)); 71 | if numt <= 2 72 | mapping(i+1) = sum(bitget(i,1:samples)); 73 | else 74 | mapping(i+1) = samples+1; 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /color_feat/colorspace.m: -------------------------------------------------------------------------------- 1 | function varargout = colorspace(Conversion,varargin) 2 | %COLORSPACE Transform a color image between color representations. 3 | % B = COLORSPACE(S,A) transforms the color representation of image A 4 | % where S is a string specifying the conversion. The input array A 5 | % should be a real full double array of size Mx3 or MxNx3. The output B 6 | % is the same size as A. 7 | % 8 | % S tells the source and destination color spaces, S = 'dest<-src', or 9 | % alternatively, S = 'src->dest'. Supported color spaces are 10 | % 11 | % 'RGB' sRGB IEC 61966-2-1 12 | % 'YCbCr' Luma + Chroma ("digitized" version of Y'PbPr) 13 | % 'JPEG-YCbCr' Luma + Chroma space used in JFIF JPEG 14 | % 'YDbDr' SECAM Y'DbDr Luma + Chroma 15 | % 'YPbPr' Luma (ITU-R BT.601) + Chroma 16 | % 'YUV' NTSC PAL Y'UV Luma + Chroma 17 | % 'YIQ' NTSC Y'IQ Luma + Chroma 18 | % 'HSV' or 'HSB' Hue Saturation Value/Brightness 19 | % 'HSL' or 'HLS' Hue Saturation Luminance 20 | % 'HSI' Hue Saturation Intensity 21 | % 'XYZ' CIE 1931 XYZ 22 | % 'Lab' CIE 1976 L*a*b* (CIELAB) 23 | % 'Luv' CIE L*u*v* (CIELUV) 24 | % 'LCH' CIE L*C*H* (CIELCH) 25 | % 'CAT02 LMS' CIE CAT02 LMS 26 | % 27 | % All conversions assume 2 degree observer and D65 illuminant. 28 | % 29 | % Color space names are case insensitive and spaces are ignored. When 30 | % sRGB is the source or destination, it can be omitted. For example 31 | % 'yuv<-' is short for 'yuv<-rgb'. 32 | % 33 | % For sRGB, the values should be scaled between 0 and 1. Beware that 34 | % transformations generally do not constrain colors to be "in gamut." 35 | % Particularly, transforming from another space to sRGB may obtain 36 | % R'G'B' values outside of the [0,1] range. So the result should be 37 | % clamped to [0,1] before displaying: 38 | % image(min(max(B,0),1)); % Clamp B to [0,1] and display 39 | % 40 | % sRGB (Red Green Blue) is the (ITU-R BT.709 gamma-corrected) standard 41 | % red-green-blue representation of colors used in digital imaging. The 42 | % components should be scaled between 0 and 1. The space can be 43 | % visualized geometrically as a cube. 44 | % 45 | % Y'PbPr, Y'CbCr, Y'DbDr, Y'UV, and Y'IQ are related to sRGB by linear 46 | % transformations. These spaces separate a color into a grayscale 47 | % luminance component Y and two chroma components. The valid ranges of 48 | % the components depends on the space. 49 | % 50 | % HSV (Hue Saturation Value) is related to sRGB by 51 | % H = hexagonal hue angle (0 <= H < 360), 52 | % S = C/V (0 <= S <= 1), 53 | % V = max(R',G',B') (0 <= V <= 1), 54 | % where C = max(R',G',B') - min(R',G',B'). The hue angle H is computed on 55 | % a hexagon. The space is geometrically a hexagonal cone. 56 | % 57 | % HSL (Hue Saturation Lightness) is related to sRGB by 58 | % H = hexagonal hue angle (0 <= H < 360), 59 | % S = C/(1 - |2L-1|) (0 <= S <= 1), 60 | % L = (max(R',G',B') + min(R',G',B'))/2 (0 <= L <= 1), 61 | % where H and C are the same as in HSV. Geometrically, the space is a 62 | % double hexagonal cone. 63 | % 64 | % HSI (Hue Saturation Intensity) is related to sRGB by 65 | % H = polar hue angle (0 <= H < 360), 66 | % S = 1 - min(R',G',B')/I (0 <= S <= 1), 67 | % I = (R'+G'+B')/3 (0 <= I <= 1). 68 | % Unlike HSV and HSL, the hue angle H is computed on a circle rather than 69 | % a hexagon. 70 | % 71 | % CIE XYZ is related to sRGB by inverse gamma correction followed by a 72 | % linear transform. Other CIE color spaces are defined relative to XYZ. 73 | % 74 | % CIE L*a*b*, L*u*v*, and L*C*H* are nonlinear functions of XYZ. The L* 75 | % component is designed to match closely with human perception of 76 | % lightness. The other two components describe the chroma. 77 | % 78 | % CIE CAT02 LMS is the linear transformation of XYZ using the MCAT02 79 | % chromatic adaptation matrix. The space is designed to model the 80 | % response of the three types of cones in the human eye, where L, M, S, 81 | % correspond respectively to red ("long"), green ("medium"), and blue 82 | % ("short"). 83 | 84 | % Pascal Getreuer 2005-2010 85 | 86 | 87 | %%% Input parsing %%% 88 | if nargin < 2, error('Not enough input arguments.'); end 89 | [SrcSpace,DestSpace] = parse(Conversion); 90 | 91 | if nargin == 2 92 | Image = varargin{1}; 93 | elseif nargin >= 3 94 | Image = cat(3,varargin{:}); 95 | else 96 | error('Invalid number of input arguments.'); 97 | end 98 | 99 | FlipDims = (size(Image,3) == 1); 100 | 101 | if FlipDims, Image = permute(Image,[1,3,2]); end 102 | if ~isa(Image,'double'), Image = double(Image)/255; end 103 | if size(Image,3) ~= 3, error('Invalid input size.'); end 104 | 105 | SrcT = gettransform(SrcSpace); 106 | DestT = gettransform(DestSpace); 107 | 108 | if ~ischar(SrcT) && ~ischar(DestT) 109 | % Both source and destination transforms are affine, so they 110 | % can be composed into one affine operation 111 | T = [DestT(:,1:3)*SrcT(:,1:3),DestT(:,1:3)*SrcT(:,4)+DestT(:,4)]; 112 | Temp = zeros(size(Image)); 113 | Temp(:,:,1) = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10); 114 | Temp(:,:,2) = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11); 115 | Temp(:,:,3) = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12); 116 | Image = Temp; 117 | elseif ~ischar(DestT) 118 | Image = rgb(Image,SrcSpace); 119 | Temp = zeros(size(Image)); 120 | Temp(:,:,1) = DestT(1)*Image(:,:,1) + DestT(4)*Image(:,:,2) + DestT(7)*Image(:,:,3) + DestT(10); 121 | Temp(:,:,2) = DestT(2)*Image(:,:,1) + DestT(5)*Image(:,:,2) + DestT(8)*Image(:,:,3) + DestT(11); 122 | Temp(:,:,3) = DestT(3)*Image(:,:,1) + DestT(6)*Image(:,:,2) + DestT(9)*Image(:,:,3) + DestT(12); 123 | Image = Temp; 124 | else 125 | Image = feval(DestT,Image,SrcSpace); 126 | end 127 | 128 | %%% Output format %%% 129 | if nargout > 1 130 | varargout = {Image(:,:,1),Image(:,:,2),Image(:,:,3)}; 131 | else 132 | if FlipDims, Image = permute(Image,[1,3,2]); end 133 | varargout = {Image}; 134 | end 135 | 136 | return; 137 | 138 | 139 | function [SrcSpace,DestSpace] = parse(Str) 140 | % Parse conversion argument 141 | 142 | if ischar(Str) 143 | Str = lower(strrep(strrep(Str,'-',''),'=','')); 144 | k = find(Str == '>'); 145 | 146 | if length(k) == 1 % Interpret the form 'src->dest' 147 | SrcSpace = Str(1:k-1); 148 | DestSpace = Str(k+1:end); 149 | else 150 | k = find(Str == '<'); 151 | 152 | if length(k) == 1 % Interpret the form 'dest<-src' 153 | DestSpace = Str(1:k-1); 154 | SrcSpace = Str(k+1:end); 155 | else 156 | error(['Invalid conversion, ''',Str,'''.']); 157 | end 158 | end 159 | 160 | SrcSpace = alias(SrcSpace); 161 | DestSpace = alias(DestSpace); 162 | else 163 | SrcSpace = 1; % No source pre-transform 164 | DestSpace = Conversion; 165 | if any(size(Conversion) ~= 3), error('Transformation matrix must be 3x3.'); end 166 | end 167 | return; 168 | 169 | 170 | function Space = alias(Space) 171 | Space = strrep(strrep(Space,'cie',''),' ',''); 172 | 173 | if isempty(Space) 174 | Space = 'rgb'; 175 | end 176 | 177 | switch Space 178 | case {'ycbcr','ycc'} 179 | Space = 'ycbcr'; 180 | case {'hsv','hsb'} 181 | Space = 'hsv'; 182 | case {'hsl','hsi','hls'} 183 | Space = 'hsl'; 184 | case {'rgb','yuv','yiq','ydbdr','ycbcr','jpegycbcr','xyz','lab','luv','lch'} 185 | return; 186 | end 187 | return; 188 | 189 | 190 | function T = gettransform(Space) 191 | % Get a colorspace transform: either a matrix describing an affine transform, 192 | % or a string referring to a conversion subroutine 193 | switch Space 194 | case 'ypbpr' 195 | T = [0.299,0.587,0.114,0;-0.1687367,-0.331264,0.5,0;0.5,-0.418688,-0.081312,0]; 196 | case 'yuv' 197 | % sRGB to NTSC/PAL YUV 198 | % Wikipedia: http://en.wikipedia.org/wiki/YUV 199 | T = [0.299,0.587,0.114,0;-0.147,-0.289,0.436,0;0.615,-0.515,-0.100,0]; 200 | case 'ydbdr' 201 | % sRGB to SECAM YDbDr 202 | % Wikipedia: http://en.wikipedia.org/wiki/YDbDr 203 | T = [0.299,0.587,0.114,0;-0.450,-0.883,1.333,0;-1.333,1.116,0.217,0]; 204 | case 'yiq' 205 | % sRGB in [0,1] to NTSC YIQ in [0,1];[-0.595716,0.595716];[-0.522591,0.522591]; 206 | % Wikipedia: http://en.wikipedia.org/wiki/YIQ 207 | T = [0.299,0.587,0.114,0;0.595716,-0.274453,-0.321263,0;0.211456,-0.522591,0.311135,0]; 208 | case 'ycbcr' 209 | % sRGB (range [0,1]) to ITU-R BRT.601 (CCIR 601) Y'CbCr 210 | % Wikipedia: http://en.wikipedia.org/wiki/YCbCr 211 | % Poynton, Equation 3, scaling of R'G'B to Y'PbPr conversion 212 | T = [65.481,128.553,24.966,16;-37.797,-74.203,112.0,128;112.0,-93.786,-18.214,128]; 213 | case 'jpegycbcr' 214 | % Wikipedia: http://en.wikipedia.org/wiki/YCbCr 215 | T = [0.299,0.587,0.114,0;-0.168736,-0.331264,0.5,0.5;0.5,-0.418688,-0.081312,0.5]*255; 216 | case {'rgb','xyz','hsv','hsl','lab','luv','lch','cat02lms'} 217 | T = Space; 218 | otherwise 219 | error(['Unknown color space, ''',Space,'''.']); 220 | end 221 | return; 222 | 223 | 224 | function Image = rgb(Image,SrcSpace) 225 | % Convert to sRGB from 'SrcSpace' 226 | switch SrcSpace 227 | case 'rgb' 228 | return; 229 | case 'hsv' 230 | % Convert HSV to sRGB 231 | Image = huetorgb((1 - Image(:,:,2)).*Image(:,:,3),Image(:,:,3),Image(:,:,1)); 232 | case 'hsl' 233 | % Convert HSL to sRGB 234 | L = Image(:,:,3); 235 | Delta = Image(:,:,2).*min(L,1-L); 236 | Image = huetorgb(L-Delta,L+Delta,Image(:,:,1)); 237 | case {'xyz','lab','luv','lch','cat02lms'} 238 | % Convert to CIE XYZ 239 | Image = xyz(Image,SrcSpace); 240 | % Convert XYZ to RGB 241 | T = [3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057]; 242 | R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3); % R 243 | G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3); % G 244 | B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3); % B 245 | % Desaturate and rescale to constrain resulting RGB values to [0,1] 246 | AddWhite = -min(min(min(R,G),B),0); 247 | R = R + AddWhite; 248 | G = G + AddWhite; 249 | B = B + AddWhite; 250 | % Apply gamma correction to convert linear RGB to sRGB 251 | Image(:,:,1) = gammacorrection(R); % R' 252 | Image(:,:,2) = gammacorrection(G); % G' 253 | Image(:,:,3) = gammacorrection(B); % B' 254 | otherwise % Conversion is through an affine transform 255 | T = gettransform(SrcSpace); 256 | temp = inv(T(:,1:3)); 257 | T = [temp,-temp*T(:,4)]; 258 | R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10); 259 | G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11); 260 | B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12); 261 | Image(:,:,1) = R; 262 | Image(:,:,2) = G; 263 | Image(:,:,3) = B; 264 | end 265 | 266 | % Clip to [0,1] 267 | Image = min(max(Image,0),1); 268 | return; 269 | 270 | 271 | function Image = xyz(Image,SrcSpace) 272 | % Convert to CIE XYZ from 'SrcSpace' 273 | WhitePoint = [0.950456,1,1.088754]; 274 | 275 | switch SrcSpace 276 | case 'xyz' 277 | return; 278 | case 'luv' 279 | % Convert CIE L*uv to XYZ 280 | WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); 281 | WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); 282 | L = Image(:,:,1); 283 | Y = (L + 16)/116; 284 | Y = invf(Y)*WhitePoint(2); 285 | U = Image(:,:,2)./(13*L + 1e-6*(L==0)) + WhitePointU; 286 | V = Image(:,:,3)./(13*L + 1e-6*(L==0)) + WhitePointV; 287 | Image(:,:,1) = -(9*Y.*U)./((U-4).*V - U.*V); % X 288 | Image(:,:,2) = Y; % Y 289 | Image(:,:,3) = (9*Y - (15*V.*Y) - (V.*Image(:,:,1)))./(3*V); % Z 290 | case {'lab','lch'} 291 | Image = lab(Image,SrcSpace); 292 | % Convert CIE L*ab to XYZ 293 | fY = (Image(:,:,1) + 16)/116; 294 | fX = fY + Image(:,:,2)/500; 295 | fZ = fY - Image(:,:,3)/200; 296 | Image(:,:,1) = WhitePoint(1)*invf(fX); % X 297 | Image(:,:,2) = WhitePoint(2)*invf(fY); % Y 298 | Image(:,:,3) = WhitePoint(3)*invf(fZ); % Z 299 | case 'cat02lms' 300 | % Convert CAT02 LMS to XYZ 301 | T = inv([0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834]); 302 | L = Image(:,:,1); 303 | M = Image(:,:,2); 304 | S = Image(:,:,3); 305 | Image(:,:,1) = T(1)*L + T(4)*M + T(7)*S; % X 306 | Image(:,:,2) = T(2)*L + T(5)*M + T(8)*S; % Y 307 | Image(:,:,3) = T(3)*L + T(6)*M + T(9)*S; % Z 308 | otherwise % Convert from some gamma-corrected space 309 | % Convert to sRGB 310 | Image = rgb(Image,SrcSpace); 311 | % Undo gamma correction 312 | R = invgammacorrection(Image(:,:,1)); 313 | G = invgammacorrection(Image(:,:,2)); 314 | B = invgammacorrection(Image(:,:,3)); 315 | % Convert RGB to XYZ 316 | T = inv([3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057]); 317 | Image(:,:,1) = T(1)*R + T(4)*G + T(7)*B; % X 318 | Image(:,:,2) = T(2)*R + T(5)*G + T(8)*B; % Y 319 | Image(:,:,3) = T(3)*R + T(6)*G + T(9)*B; % Z 320 | end 321 | return; 322 | 323 | 324 | function Image = hsv(Image,SrcSpace) 325 | % Convert to HSV 326 | Image = rgb(Image,SrcSpace); 327 | V = max(Image,[],3); 328 | S = (V - min(Image,[],3))./(V + (V == 0)); 329 | Image(:,:,1) = rgbtohue(Image); 330 | Image(:,:,2) = S; 331 | Image(:,:,3) = V; 332 | return; 333 | 334 | 335 | function Image = hsl(Image,SrcSpace) 336 | % Convert to HSL 337 | switch SrcSpace 338 | case 'hsv' 339 | % Convert HSV to HSL 340 | MaxVal = Image(:,:,3); 341 | MinVal = (1 - Image(:,:,2)).*MaxVal; 342 | L = 0.5*(MaxVal + MinVal); 343 | temp = min(L,1-L); 344 | Image(:,:,2) = 0.5*(MaxVal - MinVal)./(temp + (temp == 0)); 345 | Image(:,:,3) = L; 346 | otherwise 347 | Image = rgb(Image,SrcSpace); % Convert to sRGB 348 | % Convert sRGB to HSL 349 | MinVal = min(Image,[],3); 350 | MaxVal = max(Image,[],3); 351 | L = 0.5*(MaxVal + MinVal); 352 | temp = min(L,1-L); 353 | S = 0.5*(MaxVal - MinVal)./(temp + (temp == 0)); 354 | Image(:,:,1) = rgbtohue(Image); 355 | Image(:,:,2) = S; 356 | Image(:,:,3) = L; 357 | end 358 | return; 359 | 360 | 361 | function Image = lab(Image,SrcSpace) 362 | % Convert to CIE L*a*b* (CIELAB) 363 | WhitePoint = [0.950456,1,1.088754]; 364 | 365 | switch SrcSpace 366 | case 'lab' 367 | return; 368 | case 'lch' 369 | % Convert CIE L*CH to CIE L*ab 370 | C = Image(:,:,2); 371 | Image(:,:,2) = cos(Image(:,:,3)*pi/180).*C; % a* 372 | Image(:,:,3) = sin(Image(:,:,3)*pi/180).*C; % b* 373 | otherwise 374 | Image = xyz(Image,SrcSpace); % Convert to XYZ 375 | % Convert XYZ to CIE L*a*b* 376 | X = Image(:,:,1)/WhitePoint(1); 377 | Y = Image(:,:,2)/WhitePoint(2); 378 | Z = Image(:,:,3)/WhitePoint(3); 379 | fX = f(X); 380 | fY = f(Y); 381 | fZ = f(Z); 382 | Image(:,:,1) = 116*fY - 16; % L* 383 | Image(:,:,2) = 500*(fX - fY); % a* 384 | Image(:,:,3) = 200*(fY - fZ); % b* 385 | end 386 | return; 387 | 388 | 389 | function Image = luv(Image,SrcSpace) 390 | % Convert to CIE L*u*v* (CIELUV) 391 | WhitePoint = [0.950456,1,1.088754]; 392 | WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); 393 | WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); 394 | 395 | Image = xyz(Image,SrcSpace); % Convert to XYZ 396 | Denom = Image(:,:,1) + 15*Image(:,:,2) + 3*Image(:,:,3); 397 | U = (4*Image(:,:,1))./(Denom + (Denom == 0)); 398 | V = (9*Image(:,:,2))./(Denom + (Denom == 0)); 399 | Y = Image(:,:,2)/WhitePoint(2); 400 | L = 116*f(Y) - 16; 401 | Image(:,:,1) = L; % L* 402 | Image(:,:,2) = 13*L.*(U - WhitePointU); % u* 403 | Image(:,:,3) = 13*L.*(V - WhitePointV); % v* 404 | return; 405 | 406 | 407 | function Image = lch(Image,SrcSpace) 408 | % Convert to CIE L*ch 409 | Image = lab(Image,SrcSpace); % Convert to CIE L*ab 410 | H = atan2(Image(:,:,3),Image(:,:,2)); 411 | H = H*180/pi + 360*(H < 0); 412 | Image(:,:,2) = sqrt(Image(:,:,2).^2 + Image(:,:,3).^2); % C 413 | Image(:,:,3) = H; % H 414 | return; 415 | 416 | 417 | function Image = cat02lms(Image,SrcSpace) 418 | % Convert to CAT02 LMS 419 | Image = xyz(Image,SrcSpace); 420 | T = [0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834]; 421 | X = Image(:,:,1); 422 | Y = Image(:,:,2); 423 | Z = Image(:,:,3); 424 | Image(:,:,1) = T(1)*X + T(4)*Y + T(7)*Z; % L 425 | Image(:,:,2) = T(2)*X + T(5)*Y + T(8)*Z; % M 426 | Image(:,:,3) = T(3)*X + T(6)*Y + T(9)*Z; % S 427 | return; 428 | 429 | 430 | function Image = huetorgb(m0,m2,H) 431 | % Convert HSV or HSL hue to RGB 432 | N = size(H); 433 | H = min(max(H(:),0),360)/60; 434 | m0 = m0(:); 435 | m2 = m2(:); 436 | F = H - round(H/2)*2; 437 | M = [m0, m0 + (m2-m0).*abs(F), m2]; 438 | Num = length(m0); 439 | j = [2 1 0;1 2 0;0 2 1;0 1 2;1 0 2;2 0 1;2 1 0]*Num; 440 | k = floor(H) + 1; 441 | Image = reshape([M(j(k,1)+(1:Num).'),M(j(k,2)+(1:Num).'),M(j(k,3)+(1:Num).')],[N,3]); 442 | return; 443 | 444 | 445 | function H = rgbtohue(Image) 446 | % Convert RGB to HSV or HSL hue 447 | [M,i] = sort(Image,3); 448 | i = i(:,:,3); 449 | Delta = M(:,:,3) - M(:,:,1); 450 | Delta = Delta + (Delta == 0); 451 | R = Image(:,:,1); 452 | G = Image(:,:,2); 453 | B = Image(:,:,3); 454 | H = zeros(size(R)); 455 | k = (i == 1); 456 | H(k) = (G(k) - B(k))./Delta(k); 457 | k = (i == 2); 458 | H(k) = 2 + (B(k) - R(k))./Delta(k); 459 | k = (i == 3); 460 | H(k) = 4 + (R(k) - G(k))./Delta(k); 461 | H = 60*H + 360*(H < 0); 462 | H(Delta == 0) = nan; 463 | return; 464 | 465 | 466 | function Rp = gammacorrection(R) 467 | Rp = zeros(size(R)); 468 | i = (R <= 0.0031306684425005883); 469 | Rp(i) = 12.92*R(i); 470 | Rp(~i) = real(1.055*R(~i).^0.416666666666666667 - 0.055); 471 | return; 472 | 473 | 474 | function R = invgammacorrection(Rp) 475 | R = zeros(size(Rp)); 476 | i = (Rp <= 0.0404482362771076); 477 | R(i) = Rp(i)/12.92; 478 | R(~i) = real(((Rp(~i) + 0.055)/1.055).^2.4); 479 | return; 480 | 481 | 482 | function fY = f(Y) 483 | fY = real(Y.^(1/3)); 484 | i = (Y < 0.008856); 485 | fY(i) = Y(i)*(841/108) + (4/29); 486 | return; 487 | 488 | 489 | function Y = invf(fY) 490 | Y = fY.^3; 491 | i = (Y < 0.008856); 492 | Y(i) = (fY(i) - 4/29)*(108/841); 493 | return; 494 | --------------------------------------------------------------------------------