主頁 >  其他 > 常見顏色空間、模型及標準之演算法與源程式大全

常見顏色空間、模型及標準之演算法與源程式大全

2021-02-20 12:15:07 其他

常見顏色空間、模型及標準之演算法與源程式大全

1 常見的顏色空間與模型有:
1.1 CAM:CIECAM02 ;iCAM
1.2 CIE:XYZ (1931);RGB (1931);CAM (2002);YUV (1960);UVW (1964);CIELAB (1976);CIELUV (1976)
1.3 RGB:RGB color space;sRGB;rg chromaticity;Adobe;Wide-gamut;ProPhoto;scRGB;DCI-P3;Rec. 709;Rec. 2020;Rec. 2100
1.4 YUV:YUV (PAL);YDbDr (SECAM;PAL-N);YIQ(NTSC);YCbCr(Rec. 601;Rec. 709;Rec. 2020;Rec. 2100);ICtCp(Rec. 2100);YPbPr;xvYCC;YCoCg
1.5 Other:CcMmYK;CMY,CMYK;Coloroid;LMS;Hexachrome;HSL, HSV;HCL;Imaginary color;OSA-UCS;PCCS;RG;RYB;HWB

2 顏色系統與標準(組織):
ACES;ANPA;Colour Index International( CI list of dyes);DIC;Federal Standard 595;HKS;ICC profile;ISCC–NBS;Munsell;NCS;Ostwald;Pantone;RAL

3 影像處理主要顏色空間的數值范圍:
GRAY: 0~255
RGB: 0~255, 0~255, 0~255
YCbCr(JPEG): 0~255, 0~255, 0~255
YDbDr: 0.0~1.0, -1.333~1.333, -1.333~1.333
HSB=HSV: 0.0~360.0, 0.0~1.0, 0.0~1.0
HSL: 0.0~360.0, 0.0~1.0, 0.0~1.0
HSI: 0.0~360.0, 0.0~1.0, 0.0~1.0
CMYK: 0.0~1.0, 0.0-1.0, 0.0-1.0, 0.0~1.0
CMY: 0.0~1.0, 0.0-1.0, 0.0-1.0
YUV: 0.0~1.0, -0.436~0.436, -0.615~0.615
CIE 1931 XYZ: 0.0~0.9505, 0.0~1.0, 0.0~1.0890
CIE L*A*B*: 0.0~100.0, -128.0~127.0, -128.0~127.0
本代碼的數值范圍就是按上述規劃的,請注意不要超界哈!

4 預編譯option說明
4.1 __OTHER__ 參考代碼,不一定是對的哈!
4.2 __ORIGINAL_SOURCECODE 一般是原始代碼,對應的是稍微做了優化的;

本程式在 Visual Studio 2019 下除錯運行正常無誤,

//#define __ORIGINAL_SOURSECODE__
//#define __OTHER__
using System;
using System.Text;
using System.Drawing;

namespace Legal.ImageProcess
{
    /// <summary>
    /// 常見的顏色空間、模型及其標準的計算方法與代碼大全
    /// 1 常見的顏色空間與模型有:
    /// 1.1 CAM:CIECAM02 ;iCAM
    /// 1.2 CIE:XYZ (1931);RGB (1931);CAM (2002);YUV (1960);UVW (1964);CIELAB (1976);CIELUV (1976)
    /// 1.3 RGB:RGB color space;sRGB;rg chromaticity;Adobe;Wide-gamut;ProPhoto;scRGB;DCI-P3;Rec. 709;Rec. 2020;Rec. 2100
    /// 1.4 YUV:YUV (PAL);YDbDr (SECAM;PAL-N);YIQ(NTSC);YCbCr(Rec. 601;Rec. 709;Rec. 2020;Rec. 2100);ICtCp(Rec. 2100);YPbPr;xvYCC;YCoCg
    /// 1.5 Other:CcMmYK;CMY,CMYK;Coloroid;LMS;Hexachrome;HSL, HSV;HCL;Imaginary color;OSA-UCS;PCCS;RG;RYB;HWB
    /// 2 顏色系統與標準(組織):ACES;ANPA;Colour Index International( CI list of dyes);DIC;Federal Standard 595;HKS;ICC profile;ISCC–NBS;Munsell;NCS;Ostwald;Pantone;RAL
    /// 3 計算機圖形影像學領域主要顏色空間的數值范圍:
    ///         GRAY: 0~255
    ///          RGB: 0~255, 0~255, 0~255
    ///  YCbCr(JPEG): 0~255, 0~255, 0~255
    ///        YDbDr: 0.0~1.0, -1.333~1.333, -1.333~1.333
    ///      HSB=HSV: 0.0~360.0, 0.0~1.0, 0.0~1.0
    ///          HSL: 0.0~360.0, 0.0~1.0, 0.0~1.0
    ///          HSI: 0.0~360.0, 0.0~1.0, 0.0~1.0
    ///         CMYK: 0.0~1.0, 0.0-1.0, 0.0-1.0, 0.0~1.0
    ///          CMY: 0.0~1.0, 0.0-1.0, 0.0-1.0
    ///          YUV: 0.0~1.0, -0.436~0.436, -0.615~0.615
    /// CIE 1931 XYZ: 0.0~0.9505, 0.0~1.0, 0.0~1.0890
    ///   CIE L*A*B*: 0.0~100.0, -128.0~127.0, -128.0~127.0
    ///   本代碼的數值范圍就是按上述規劃的,請注意不要超界哈!
    /// 4 預編譯option說明
    /// 4.1 __OTHER__ 參考代碼,不一定是對的哈!
    /// 4.2 __ORIGINAL_SOURCECODE 一般是原始代碼,對應的是稍微做了優化的;
    /// 5 Easter eggs
    /// 5.1 Visit www.Legalsoft.com.cn TO FIND MAGICAL IMAGE-PROCESSING SOFTWARES.
    /// </summary>
    public static class ColorspaceHelper
    {
        public static byte[] FromColor(Color c)
        {
            return new byte[3] { c.R, c.G, c.B };
        }

        public static Color ToColor(byte R, byte G, byte B)
        {
            return Color.FromArgb(R, G, B);
        }

        /// <summary>
        /// RGB顏色值的規范化(0-255)(避免超界)
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        public static byte Clamp(int v)
        {
            return (byte)((v > 255) ? 255 : (v < 0) ? 0 : v);
        }

        /// <summary>
        /// RGB顏色值的規范化(浮點數,0.0-255.0)
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        public static double Clamp(double v)
        {
            return ((v > 255.0) ? 255.0 : (v < 0.0) ? 0.0 : v);
        }

        #region 灰度Gray的多種實用計算方法
        /// <summary>
        /// RGB計算灰度值
        /// (常見演算法:據說按一個很著名的心理學公式)
        /// 灰度計算等于有損壓縮,必然要損失一些細節,
        /// 實際上計算灰度是沒有最好公式的,可以根據情況選擇與分布這些引數,
        /// 比如紅黑色的圖片,那么公式是:R*0.8+G*0.1+B*0.1,也未嘗不可,
        /// 基于統計的灰度計算是最合理的,可以使得細節的損失盡量減少,但計算量會大一些,
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>(0-255)</returns>
        public static byte RGB2GRAY(byte R, byte G, byte B)
        {
            int c = (int)(R * 0.299 + G * 0.587 + B * 0.114);
            return Clamp(c);
        }

        /// <summary>
        /// 按RGB平均值法計算灰度值
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>(0-255)</returns>
        public static byte RGB2GRAY_AVERAGE(byte R, byte G, byte B)
        {
#if __ORIGINAL_SOURSECODE__
            int c = (int)((R + G + B) / 3.0);
#else
            int c = (int)((R + G + B) * 0.333333333333333333);
#endif
            return Clamp(c);
        }

        /// <summary>
        /// 適應Gamma校正的灰度計算公式
        /// https://baike.baidu.com/item/%E7%81%B0%E5%BA%A6%E5%8C%96
        /// 為什么是2.2?
        /// 將人眼感受到的灰階轉換成自然界真實的灰階,
        /// 比如:(0.5)^2.2=0.2
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns></returns>
        public static byte RGB2GRAY_GAMMA(byte R, byte G, byte B)
        {
            double r = Math.Pow(R, 2.2);
            double g = Math.Pow(G * 1.5, 2.2);
            double b = Math.Pow(B * 0.6, 2.2);
#if __ORIGINAL_SOURSECODE__
            double av = Math.Pow((r + g + b) / (1.0 + Math.Pow(1.5, 2.2) + Math.Pow(0.6, 2.2)), 1.0 / 2.2);
#else
            double av = Math.Pow((r + g + b) * 0.265597304794686000, 0.454545454545455000);
#endif
            return Clamp((int)av);
        }

        private static double[] tableGamma = null;
        private static double[] tableGammaRev = null;
        /// <summary>
        /// 適用于快速查表演算法的Gamma指數表及開方表
        /// Math.Pow(x,2.2) 與 Math.Pow(x,1.0/2.2)
        /// </summary>
        private static void TableGamma()
        {
            tableGamma = new double[384];
            tableGammaRev = new double[384];
            for (int i = 0; i < 385; i++)
            {
                tableGamma[i] = Math.Pow(i, 2.2);
#if __ORIGINAL_SOURSECODE__
                tableGammaRev[i] = Math.Pow(i, 1.0 / 2.2);
#else
                tableGammaRev[i] = Math.Pow(i, 0.454545454545455000);
#endif
            }
        }

        /// <summary>
        /// 適應Gamma校正的灰度值快速查表演算法
        /// (不用求冪,近似演算法,有誤差)
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns></returns>
        public static byte RGB2GRAY_GAMMA_QUICK(byte R, byte G, byte B)
        {
            if (tableGamma == null || tableGammaRev == null)
            {
                TableGamma();
            }
            double aR = tableGamma[(int)R];
            double aG = tableGamma[(int)(G * 1.5)];
            double aB = tableGamma[(int)(B * 0.6)];
#if __ORIGINAL_SOURSECODE__
            double av = tableGammaRev[(int)((aR + aG + aB) / (1.0 + Math.Pow(1.5, 2.2) + Math.Pow(0.6, 2.2)))];
#else
            double av = tableGammaRev[(int)((aR + aG + aB) * 0.265597304794686000)];
#endif
            return Clamp((int)av);
        }

        /// <summary>
        /// Photoshop(RGB1998)灰度計算方法
        /// gamma = 2.20
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns></returns>
        public static byte RGB2GRAY_PHOTOSHOP(byte R, byte G, byte B)
        {
            double aR = Math.Pow(R, 2.2) * 0.2973;
            double aG = Math.Pow(G, 2.2) * 0.6274;
            double aB = Math.Pow(B, 2.2) * 0.0753;
#if __ORIGINAL_SOURSECODE__
            double av = Math.Pow(aR + aG + aB, 1 / 2.2);
#else
            double av = Math.Pow(aR + aG + aB, 0.454545454545455000);
#endif
            return Clamp((int)av);
        }

        /// <summary>
        /// Photoshop(RGB1998)灰度計算的快速查表演算法
        /// gamma = 2.20
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns></returns>
        public static byte RGB2GRAY_PHOTOSHOP_QUICK(byte R, byte G, byte B)
        {
            if (tableGamma == null || tableGammaRev == null)
            {
                TableGamma();
            }
            double aR = tableGamma[R] * 0.2973;
            double aG = tableGamma[G] * 0.6274;
            double aB = tableGamma[B] * 0.0753;
            double av = tableGammaRev[(int)(aR + aG + aB)];
            return Clamp((int)av);
        }

        /// <summary>
        /// 按RGB計算灰度值(浮點數)
        /// </summary>
        /// <param name="R">(0-255) or (0-1.0)</param>
        /// <param name="G">(0-255) or (0-1.0)</param>
        /// <param name="B">(0-255) or (0-1.0)</param>
        /// <returns>(0-255) or (0-1.0)</returns>
        public static double RGB2GRAY(double R, double G, double B)
        {
            return Clamp(R * 0.299 + G * 0.587 + B * 0.114);
        }
        #endregion

        #region RGB <-> YUV
        /// <summary>
        /// RGB 轉 YUV(YUV444)
        /// YUV是三個分量,Y表示明亮度(Luminance或Luma),也就是灰度值,
        /// U和V表示的是彩色資訊,分別為色度和濃度(Chrominance和Chroma),
        /// </summary>
        /// <param name="R">byte(0-255)</param>
        /// <param name="G">byte(0-255)</param>
        /// <param name="B">byte(0-255)</param>
        /// <returns>double[3](Y:0-1,U:-0.436~0.436,V:-0.615~0.615)</returns>
        public static double[] RGB2YUV(byte R, byte G, byte B)
        {
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;
            double Y = +0.299 * r + 0.587 * g + 0.114 * b;
            double U = -0.148 * r - 0.289 * g + 0.437 * b;
            double V = +0.615 * r - 0.515 * g - 0.100 * b;
            return new double[3] { Y, U, V };
        }
        public static double[] RGB2YUV(byte[] RGB)
        {
            return RGB2YUV(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// YUV(YUV444) 轉 RGB
        /// https://blog.csdn.net/timini/article/details/78949768
        /// </summary>
        /// <param name="Y">(0,1.0)</param>
        /// <param name="U">(-0.436,+0.436)</param>
        /// <param name="V">(-0.615,+0.615)</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] YUV2RGB(double Y, double U, double V)
        {
            int R = (int)((Y + 1.1398373983739837400 * V) * 255.0);
            int G = (int)((Y - 0.3946517043589703515 * U - 0.5805986066674976801 * V) * 255.0);
            int B = (int)((Y + 2.0321100917431192660 * U) * 255.0);
            return new byte[3] { Clamp(R), Clamp(G), Clamp(B) };
        }
        public static byte[] YUV2RGB(double[] YUV)
        {
            return YUV2RGB(YUV[0], YUV[1], YUV[2]);
        }
        #endregion

        #region RGB <-> YCbCr
        /// <summary>
        /// RGB 轉 YCbCr(YIO,YPbPr)
        /// YCbCr(簡稱YCC)中,Y代表亮度,Cb和Cr藍色(blue)和紅色(red)的色度,
        /// YCbCr是YUV的壓縮和偏移的版本,
        /// 演算法好多, 選擇JPEG演算法,
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/14/2911309.html
        /// </summary>
        /// <param name="R">byte(0-255)</param>
        /// <param name="G">byte(0-255)</param>
        /// <param name="B">byte(0-255)</param>
        /// <returns>byte[3]{Y,Cb,Cr}</returns>
        public static byte[] RGB2YCbCr(byte R, byte G, byte B)
        {
            // JPEG conversion Method
            int Ya = (int)(000 + 0.299000 * R + 0.587000 * G + 0.114000 * B);
            int Cb = (int)(128 - 0.168736 * R - 0.331264 * G + 0.500000 * B);
            int Cr = (int)(128 + 0.500000 * R - 0.418688 * G - 0.081312 * B);
            return new byte[3] { Clamp(Ya), Clamp(Cb), Clamp(Cr) };
        }
        public static byte[] RGB2YCbCr(byte[] RGB)
        {
            return RGB2YCbCr(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// YCbCr(YIO) 轉 RGB
        /// YCbCr(簡稱YCC)中,Y代表亮度,Cb和Cr藍色(blue)和紅色(red)的色度,
        /// YCbCr是YUV的壓縮和偏移的版本,
        /// 演算法好多, 選擇JPEG演算法,
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/14/2911309.html
        /// </summary>
        /// <param name="Y">byte(0-255)</param>
        /// <param name="Cb">byte(0-255)</param>
        /// <param name="Cr">byte(0-255)</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] YCbCr2RGB(byte Y, byte Cb, byte Cr)
        {
            // JPEG conversion Method
            int R = (int)(Y + 1.402000 * (Cr - 128));
            int G = (int)(Y - 0.344136 * (Cb - 128) - 0.714136 * (Cr - 128));
            int B = (int)(Y + 1.772000 * (Cb - 128));
            return new byte[3] { Clamp(R), Clamp(G), Clamp(B) };
        }
        public static byte[] YCbCr2RGB(byte[] YCbCr)
        {
            return YCbCr2RGB(YCbCr[0], YCbCr[1], YCbCr[2]);
        }
        #endregion

        #region RGB <-> YDbDr
        /// <summary>
        /// RGB 轉 YDbDr
        /// YDbDr也類似YCbCr,同樣也是色度坐標不同,
        /// YDbDr是SECAM制式電視系統所用的顏色模型,
        /// https://en.wikipedia.org/wiki/YDbDr
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/15/2912907.html
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>double[3]{Y:0-1,Db:-1.333~1.333,Dr:-1.333~1.333}</returns>
        public static double[] RGB2YDbDr(byte R, byte G, byte B)
        {
            //https://en.wikipedia.org/wiki/YDbDr
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;
            double Ya = +0.299 * r + 0.587 * g + 0.114 * b;
            double Db = -0.450 * r - 0.883 * g + 1.333 * b;
            double Dr = -1.333 * r + 1.116 * g + 0.217 * b;
            // laviewpbt修改
            //double Ya = 0.2990 * R - 0.1688 * G - 0.5000 * B;
            //double Db = 0.5870 * R - 0.3312 * G + 0.4186 * B;
            //double Dr = 0.1140 * R + 0.5000 * G + 0.0814 * B;
            return new double[3] { Ya, Db, Dr };
        }
        public static double[] RGB2YDbDr(byte[] RGB)
        {
            return RGB2YDbDr(RGB[0], RGB[1], RGB[2]);
        }

#if __OTHER__
        /// <summary>
        /// RGB 轉 YDbDr
        /// https://blog.csdn.net/leansmall/article/details/78896937
        /// </summary>
        /// <param name="R">0-255</param>
        /// <param name="G">0-255</param>
        /// <param name="B">0-255</param>
        /// <returns>byte[3]{Y:0-255,Db:0-255,Dr:0-255}</returns>
        public static byte[] RGB2YDbDr_OTHER(byte R, byte G, byte B)
        {
            double Y = 0.2990 * R - 0.1688 * G - 0.5000 * B;
            double Db = 0.5870 * R - 0.3312 * G + 0.4186 * B;
            double Dr = 0.1140 * R + 0.5000 * G + 0.0814 * B;
            return new byte[3] { Clamp((int)Y), Clamp((int)Db), Clamp((int)Dr) };
        }
#endif
        /// <summary>
        /// YDbDr 轉 RGB
        /// YDbDr也類似YCbCr,同樣也是色度坐標不同,
        /// YDbDr是SECAM制式電視系統所用的顏色模型,
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/15/2912907.html
        /// </summary>
        /// <param name="Y">0.0-1.0</param>
        /// <param name="Db">-1.333~1.333</param>
        /// <param name="Dr">-1.333~1.333</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] YDbDr2RGB(double Y, double Db, double Dr)
        {
            //https://en.wikipedia.org/wiki/YDbDr
            double R = Y + 0.000092303716148 * Db - 0.525912630661865 * Dr;
            double G = Y - 0.129132898890509 * Db + 0.267899328207599 * Dr;
            double B = Y + 0.664679059978955 * Db - 0.000079202543533 * Dr;

            // laviewpbt修改
            // 需要調整 1.333 倍
            //double R = Y + 0.000246081707249 * Db - 1.402083073344533 * Dr;
            //double G = Y - 0.344268308442098 * Db + 0.714219609001458 * Dr;
            //double B = Y + 1.772034373903893 * Db - 0.000211153981059 * Dr;

            R *= 255.0;
            G *= 255.0;
            B *= 255.0;
            return new byte[3] { Clamp((int)R), Clamp((int)G), Clamp((int)B) };
        }
        public static byte[] YDbDr2RGB(double[] YDbDr)
        {
            return YDbDr2RGB(YDbDr[0], YDbDr[1], YDbDr[2]);
        }

#if __OTHER__
        /// <summary>
        /// YDbDr 轉 RGB
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/15/2912907.html
        /// </summary>
        /// <param name="Y">0-255</param>
        /// <param name="Db">0-255</param>
        /// <param name="Dr">0-255</param>
        /// <returns></returns>
        public static byte[] YDbDr2RGB_OTHER(byte Y, byte Db, byte Dr)
        {
            double R = Y + 0.000246081707249 * Db - 1.402083073344533 * Dr;
            double G = Y - 0.344268308442098 * Db + 0.714219609001458 * Dr;
            double B = Y + 1.772034373903893 * Db - 0.000211153981059 * Dr;
            return new byte[3] { Clamp((int)R), Clamp((int)G), Clamp((int)B) };
        }
#endif
        #endregion

        #region RGB <-> HSV
        /// <summary>
        /// RGB 轉 HSV(Hue,Saturation,Value)
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>[3]{H:0-360,S:0-1,V:0-1}</returns>
        public static double[] RGB2HSV(byte R, byte G, byte B)
        {
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;
            double H, S, V;
            double min = Math.Min(r, Math.Min(g, b));
            double max = Math.Max(r, Math.Max(g, b));
            double delta = max - min;
            V = max;
            // max != 0
            if (Math.Abs(max) > float.Epsilon)
            {
                S = delta / max;
            }
            else
            {
                // r = g = b = 0 
                // s = 0, v 未定義 
                S = 0.0;
                H = -1.0;
                return new double[3] { H, S, V };
            }

            H = 0.0;
            if (Math.Abs(delta) > float.Epsilon)
            {
                if (Math.Abs(r - max) < float.Epsilon)
                {
                    // 在 yellow & magenta 之間 
                    H = (g - b) / delta;
                }
                else if (Math.Abs(g - max) < float.Epsilon)
                {
                    // 在 cyan & yellow 之間 
                    H = 2.0 + (b - r) / delta;
                }
                else
                {
                    // 在 magenta & cyan 之間 
                    H = 4.0 + (r - g) / delta;
                }
            }
            // 角度 
            H *= 60.0;
            if (H < 0.0)
            {
                H += 360.0;
            }
            return new double[3] { H, S, V };
        }
        public static double[] RGB2HSV(byte[] RGB)
        {
            return RGB2HSV(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// HSV(Hue,Saturation,Value) 轉 RGB
        /// HSV中的V表示明度(Value/Brightness),
        /// 根據縮寫不同,HSV有時也被稱作HSB(就是說HSV和HSB是一回事),
        /// </summary>
        /// <param name="H">(0.0-360.0)</param>
        /// <param name="S">(0.0-1.0)</param>
        /// <param name="V">(0.0-1.0)</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] HSV2RGB(double H, double S, double V)
        {
            double R, G, B;
            if (Math.Abs(S) < float.Epsilon)
            {
                // 灰度 
                // R = G = B = V;
                int vi = (int)(V * 255.0);
                return new byte[3] { Clamp(vi), Clamp(vi), Clamp(vi) };
            }

            H /= 60.0;
            int i = (int)Math.Floor(H);
            double f = H - i;
            double p = V * (1 - S);
            double q = V * (1 - S * f);
            double t = V * (1 - S * (1 - f));
            // 按扇區 0 到 5 賦值
            switch (i)
            {
                case 0:
                    R = V;
                    G = t;
                    B = p;
                    break;
                case 1:
                    R = q;
                    G = V;
                    B = p;
                    break;
                case 2:
                    R = p;
                    G = V;
                    B = t;
                    break;
                case 3:
                    R = p;
                    G = q;
                    B = V;
                    break;
                case 4:
                    R = t;
                    G = p;
                    B = V;
                    break;
                default:
                    R = V;
                    G = p;
                    B = q;
                    break;
            }
            int ri = (int)(R * 255.0);
            int gi = (int)(G * 255.0);
            int bi = (int)(B * 255.0);
            return new byte[3] { Clamp(ri), Clamp(gi), Clamp(bi) };
        }
        public static byte[] HSV2RGB(double[] HSV)
        {
            return HSB2RGB(HSV);
        }

        #endregion

        #region RGB <-> HSL
        /// <summary>
        /// RGB 轉 HSL(Hue,Saturation,Lightness=Luminance)
        /// https://blog.csdn.net/timini/article/details/78949768
        /// </summary>
        /// <param name="R">(0,255)</param>
        /// <param name="G">(0,255)</param>
        /// <param name="B">(0,255)</param>
        /// <returns>double[3]{H:0-360,S:0.0-1.0,L:0.0-1.0}</returns>
        public static double[] RGB2HSL(byte R, byte G, byte B)
        {
            // 換算0-1
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;
            double max = Math.Max(r, Math.Max(g, b));
            double min = Math.Min(r, Math.Min(g, b));
            double H = 0.0, S = 0.0, L = 0.0;
            // Hue 
            // max = min
            if (Math.Abs(max - min) < float.Epsilon)
            {
                H = 0.0; // undefined 
            }
            else if (Math.Abs(max - r) < float.Epsilon && g >= b)
            {
                H = 60.0 * (g - b) / (max - min);
            }
            else if (Math.Abs(max - r) < float.Epsilon && g < b)
            {
                H = 60.0 * (g - b) / (max - min) + 360.0;
            }
            else if (Math.Abs(max - g) < float.Epsilon)
            {
                H = 60.0 * (b - r) / (max - min) + 120.0;
            }
            else if (Math.Abs(max - b) < float.Epsilon)
            {
                H = 60.0 * (r - g) / (max - min) + 240.0;
            }
            // Luminance 
            L = (max + min) / 2.0;
            // Saturation 
            if (Math.Abs(L) < float.Epsilon || Math.Abs(max - min) < float.Epsilon)
            {
                S = 0.0;
            }
            else if (0 < L && L <= 0.5)
            {
                S = (max - min) / (max + min);
            }
            else if (L > 0.5)
            {
                S = (max - min) / (2 - (max + min));
            }
            return new double[3] { H, S, L };
        }
        public static double[] RGB2HSL(byte[] RGB)
        {
            return RGB2HSL(RGB[0], RGB[1], RGB[2]);
        }

#if __OTHER__
        /// <summary>
        /// RGB 轉 HSL
        /// https://blog.csdn.net/qq_35247586/article/details/109919637
        /// </summary>
        /// <param name="R">(0,255)</param>
        /// <param name="G">(0,255)</param>
        /// <param name="B">(0,255)</param>
        /// <returns>double[3]{H:0-360,S:0-1,L:0-1}</returns>
        public static double[] RGB2HSL(byte R, byte G, byte B)
        {
            // normalize red, green, blue values 
            double r = (double)R / 255.0;
            double g = (double)G / 255.0;
            double b = (double)B / 255.0;
            double max = Math.Max(r, Math.Max(g, b));
            double min = Math.Min(r, Math.Min(g, b));
            double delta = max - min;
            double sum = max + min;

            double L = sum / 2.0;
            // delta==0, max==min
            if (Math.Abs(delta) < float.Epsilon)
            {
                return new double[3] { 0, 0, L };
            }
            double S = delta / ((sum < 1.0) ? sum : (2.0 - sum));
            double H = 0.0;
            if (Math.Abs(r - max) < float.Epsilon) { H = (g - b) / delta / 6.0; }
            else if (Math.Abs(g - max) < float.Epsilon) { H = (2.0 + (b - r) / delta) / 6.0; }
            else { H = (4.0 + (r - g) / delta) / 6.0; }
            if (H < 0) H += 1.0;

            // 原文輸出 H,0--1.0;這里轉換為 0--360
            H *= 360.0;
            return new double[3] { H, S, L };
        }
#endif

        /// <summary>
        /// HSL 轉 RGB
        /// https://blog.csdn.net/qq_35247586/article/details/109919637
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1.0</param>
        /// <param name="L">0-1.0</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] HSL2RGB(double H, double S, double L)
        {
            // 原文是 0-1,這里轉換一下,
            H /= 360.0;

            if (Math.Abs(S) < float.Epsilon)
            {
                L = L * 255.0;
                return new byte[3] { Clamp((int)L), Clamp((int)L), Clamp((int)L) };
            }
            double q = (L <= 0.5) ? (L * (1.0 + S)) : (L + S - (L * S));
            if (q <= 0.0)
            {
                return new byte[3] { 0, 0, 0 };
            }

            double m = L + L - q;
            double v = (q - m) / q;
            if (Math.Abs(H - 1.0) < float.Epsilon) H = 0.0;
            H *= 6.0;
            int n = (int)H;
            double f = H - n;
            double t = q * v * f;
            double y = m + t;
            double z = q - t;
            double R = 0.0, G = 0.0, B = 0.0;
            switch (n)
            {
                case 0: R = q; G = y; B = m; break;
                case 1: R = z; G = q; B = m; break;
                case 2: R = m; G = q; B = y; break;
                case 3: R = m; G = z; B = q; break;
                case 4: R = y; G = m; B = q; break;
                case 5: R = q; G = m; B = z; break;
            }
            R *= 255.0;
            G *= 255.0;
            B *= 255.0;
            return new byte[3] { Clamp((int)R), Clamp((int)G), Clamp((int)B) };
        }
        public static byte[] HSL2RGB(double[] HSL)
        {
            return HSL2RGB(HSL[0], HSL[1], HSL[2]);
        }

#if __OTHER__
        /// <summary>
        /// HSL(Hue,Saturation,Lightness=Luminance) 轉 RGB. 
        /// https://blog.csdn.net/timini/article/details/78949768
        /// </summary>
        /// <param name="H">Hue, must be in [0, 360].</param> 
        /// <param name="S">Saturation, must be in [0, 1].</param> 
        /// <param name="L">Luminance, must be in [0, 1].</param> 
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] HSL2RGB(double H, double S, double L)
        {
            if (Math.Abs(S) < float.Epsilon)
            {
                // achromatic color (gray scale) 
                byte aL = (byte)(L * 255.0);
                return new byte[3] { aL, aL, aL };
            }
            else
            {
                double q = (L < 0.5) ? (L * (1.0 + S)) : (L + S - (L * S));
                double p = (2.0 * L) - q;
                double Hk = H / 360.0;
                double[] T = new double[3];
                T[0] = Hk + (1.0 / 3.0); // Tr 
                T[1] = Hk; // Tb 
                T[2] = Hk - (1.0 / 3.0); // Tg 
                for (int i = 0; i < 3; i++)
                {
                    if (T[i] < 0) T[i] += 1.0;
                    if (T[i] > 1) T[i] -= 1.0;
                    if ((T[i] * 6) < 1)
                    {
                        T[i] = p + ((q - p) * 6.0 * T[i]);
                    }
                    else if ((T[i] * 2.0) < 1) //(1.0/6.0)<=T[i] && T[i]<0.5 
                    {
                        T[i] = q;
                    }
                    else if ((T[i] * 3.0) < 2) // 0.5<=T[i] && T[i]<(2.0/3.0) 
                    {
                        T[i] = p + (q - p) * ((2.0 / 3.0) - T[i]) * 6.0;
                    }
                    else
                    {
                        T[i] = p;
                    }

                    T[i] *= 255.0;
                }
                return new byte[3] { Clamp((int)T[0]), Clamp((int)T[1]), Clamp((int)T[2]) };
            }
        }
#endif
        #endregion

        #region RGB <-> HSB
        /// <summary>
        /// RGB 轉 HSB(Hue, Saturation, Brightness)
        /// HSV中的V表示明度(Value/Brightness),
        /// 根據縮寫不同,HSV有時也被稱作HSB(就是說HSV和HSB是一回事),
        /// https://blog.csdn.net/timini/article/details/78949768
        /// https://www.cnblogs.com/Free-Thinker/p/4950364.html
        /// </summary> 
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>double[3]{H:0-360,S:0-1.0,B:0-1.0}</returns>
        public static double[] RGB2HSB(byte R, byte G, byte B)
        {
            // Normalize red, green and blue values 
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;
            // conversion start 
            double max = Math.Max(r, Math.Max(g, b));
            double min = Math.Min(r, Math.Min(g, b));
            double H = 0.0;
            if (Math.Abs(max - min) > 0.0)
            {
                if (Math.Abs(max - r) < float.Epsilon && g >= b)
                {
                    H = 60.0 * (g - b) / (max - min);
                }
                else if (Math.Abs(max - r) < float.Epsilon && g < b)
                {
                    H = 60.0 * (g - b) / (max - min) + 360.0;
                }
                else if (Math.Abs(max - g) < float.Epsilon)
                {
                    H = 60.0 * (b - r) / (max - min) + 120.0;
                }
                else if (Math.Abs(max - b) < float.Epsilon)
                {
                    H = 60.0 * (r - g) / (max - min) + 240.0;
                }
            }
            double S = (Math.Abs(max) < float.Epsilon) ? 0.0 : (1.0 - (min / max));
            double V = max;
            return new double[3] { H, S, V };
        }
        public static double[] RGB2HSB(byte[] RGB)
        {
            return RGB2HSB(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// HSB 轉 RGB
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1</param>
        /// <param name="B">0-1</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] HSB2RGB(double H, double S, double B)
        {
            double r = 0.0;
            double g = 0.0;
            double b = 0.0;

            if (Math.Abs(S) < float.Epsilon)
            {
                r = g = b = B;
            }
            else
            {
                // the color wheel consists of 6 sectors. Figure out which sector you're in.
                double sectorPos = H / 60.0;
                int sectorNumber = (int)(Math.Floor(sectorPos));
                // get the fractional part of the sector
                double fractionalSector = sectorPos - sectorNumber;

                // calculate values for the three axes of the color.
                double p = B * (1.0 - S);
                double q = B * (1.0 - (S * fractionalSector));
                double t = B * (1.0 - (S * (1.0 - fractionalSector)));

                // assign the fractional colors to r, g, and b based on the sector the angle is in.
                switch (sectorNumber)
                {
                    case 0:
                        r = B;
                        g = t;
                        b = p;
                        break;
                    case 1:
                        r = q;
                        g = B;
                        b = p;
                        break;
                    case 2:
                        r = p;
                        g = B;
                        b = t;
                        break;
                    case 3:
                        r = p;
                        g = q;
                        b = B;
                        break;
                    case 4:
                        r = t;
                        g = p;
                        b = B;
                        break;
                    case 5:
                        r = B;
                        g = p;
                        b = q;
                        break;
                }
            }
            r *= 255.0;
            g *= 255.0;
            b *= 255.0;
            return new byte[3] { Clamp((int)r), Clamp((int)g), Clamp((int)b) };
        }
        public static byte[] HSB2RGB(double[] HSB)
        {
            return HSB2RGB(HSB[0], HSB[1], HSB[2]);
        }

        #endregion

        #region RGB <-> HSI
        /// <summary>
        /// RGB 轉 HSI
        /// HSI顏色空間是從人的視覺系統出發,
        /// 用色調(Hue)、色飽和度(Saturation或Chroma)
        /// 和亮度(Intensity或Brightness)來描述色彩,
        /// https://github.com/DrNickRedfern/RGB2HSI
        /// http://fourier.eng.hmc.edu/e161/lectures/ColorProcessing/node3.html
        /// </summary>
        /// <param name="R">0-255</param>
        /// <param name="G">0-255</param>
        /// <param name="B">0-255</param>
        /// <returns>double[3]{H:0-360,S:0-1,I:0-1}</returns>
        public static double[] RGB2HSI(byte R, byte G, byte B)
        {
#if __OTHER__
            //https://github.com/DrNickRedfern/RGB2HSI
            // 原文是 0-1,規范化一下適用于 0-255
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;
            double va = ((r - g) + (r - b)) / 2.0;
            double kb = (r - g) * (r - g) + (r - b) * (g - b);
            if (Math.Abs(kb) < float.Epsilon) { return new double[3] { 0.0, 0.0, r }; }
            double vb = Math.Sqrt(kb);
            double theta = Math.Acos(va / vb);
            double H = (b <= g) ? theta : (2.0 * Math.PI - theta);
            if (Math.Abs(r + g + b) < float.Epsilon) { return new double[3] { 0.0, 0.0, 0.0 }; }
            double S = 1.0 - 3.0 * Math.Min(r, Math.Min(g, b)) / (r + g + b);
            double I = (r + g + b) / 3.0;
            H = H * 180.0 / Math.PI;
            return new double[3] { H, S, I };
#endif
            //http://fourier.eng.hmc.edu/e161/lectures/ColorProcessing/node3.html
            double I = (R + G + B);
            if (Math.Abs(I) < float.Epsilon) { return new double[3] { 0.0, 0.0, 0.0 }; }
            double r = R / I;
            double g = G / I;
            double b = B / I;
            I = I / 3.0;
            if (Math.Abs(R - G) < float.Epsilon && Math.Abs(G - B) < float.Epsilon)
            {
                return new double[3] { 0.0, 0.0, I };
            }
            double w = 0.5 * (R - G + R - B) / Math.Sqrt((R - G) * (R - G) + (R - B) * (G - B));
            if (w > 1.0) w = 1.0;
            if (w < -1.0) w = -1.0;
            double H = Math.Acos(w);
            if (B > G) H = 2.0 * Math.PI - H;
            double S = 0.0;
            if (r <= g && r <= b) S = 1.0 - 3.0 * r;
            if (g <= r && g <= b) S = 1.0 - 3.0 * g;
            if (b <= r && b <= g) S = 1.0 - 3.0 * b;
            H = H * 180.0 / Math.PI;
            return new double[3] { H, S, I };
        }
        public static double[] RGB2HSI(byte[] RGB)
        {
            return RGB2HSI(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// HSI 轉 RGB
        /// HSI顏色空間是從人的視覺系統出發,
        /// 用色調(Hue)、色飽和度(Saturation或Chroma)
        /// 和亮度(Intensity或Brightness)來描述色彩,
        /// http://fourier.eng.hmc.edu/e161/lectures/ColorProcessing/node3.html
        /// </summary>
        /// <param name="H">0.0-360.0</param>
        /// <param name="S">0.0-1.0</param>
        /// <param name="I">0.0-1.0</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] HSI2RGB(double H, double S, double I)
        {
#if __OTHER__
            // 這段代碼不嚴謹!
            double aB = I * (1.0 - S);
            double aR = I * (1.0 + (S * Math.Cos(H * Math.PI / 180.0)) / (Math.Cos((60.0 - H) * Math.PI / 180.0)));
            double aG = 3.0 * I - (aR + aB);
            // 原文是 0-1,規范化一下適用于 0-255
            aR *= 255.0; aG *= 255.0; aB *= 255.0;
            return new byte[3] { Clamp((int)aR), Clamp((int)aG), Clamp((int)aB) };
#endif
            //http://fourier.eng.hmc.edu/e161/lectures/ColorProcessing/node3.html
            H *= Math.PI / 180.0;
            double r = 0.0, g = 0.0, b = 0.0;
            // S = 0
            if (Math.Abs(S) < float.Epsilon)
            {
                r = g = b = I;
            }
            else
            {
                if ((H >= 0) && (H < 2.0 * Math.PI / 3.0))
                {
                    b = (1.0 - S) / 3.0;
                    r = (1.0 + S * Math.Cos(H) / Math.Cos(Math.PI / 3.0 - H)) / 3.0;
                    g = 1.0 - r - b;
                }
                else if ((H >= 2.0 * Math.PI / 3.0) && (H < 4.0 * Math.PI / 3.0))
                {
                    H = H - 2.0 * Math.PI / 3.0;
                    r = (1.0 - S) / 3.0;
                    g = (1.0 + S * Math.Cos(H) / Math.Cos(Math.PI / 3.0 - H)) / 3.0;
                    b = 1.0 - r - g;
                }
                else if ((H >= 4.0 * Math.PI / 3.0) && (H < 2.0 * Math.PI))
                {
                    H = H - 4.0 * Math.PI / 3.0;
                    g = (1.0 - S) / 3.0;
                    b = (1.0 + S * Math.Cos(H) / Math.Cos(Math.PI / 3.0 - H)) / 3.0;
                    r = 1.0 - b - g;
                }
                else
                {
                    // H out of range!
                }
                r = r * 3.0 * I;
                g = g * 3.0 * I;
                b = b * 3.0 * I;
            }
            r *= 255.0; g *= 255.0; b *= 255.0;
            return new byte[3] { Clamp((int)r), Clamp((int)g), Clamp((int)b) };
        }
        public static byte[] HSI2RGB(double[] HSI)
        {
            return HSI2RGB(HSI[0], HSI[1], HSI[2]);
        }
        #endregion

        #region RGB <-> CMY
        /// <summary>
        /// RGB 轉 CMY
        /// </summary>
        /// <param name="R"></param>
        /// <param name="G"></param>
        /// <param name="B"></param>
        /// <returns>double[3]{C:0-1,M:0-1,Y:0-1}</returns>
        public static double[] RGB2CMY(byte R, byte G, byte B)
        {
            return new double[3] {
                (1.0 - R/255.0),
                (1.0 - G/255.0),
                (1.0 - B/255.0)
            };
        }
        public static double[] RGB2CMY(byte[] RGB)
        {
            return RGB2CMY(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// CMY 轉 RGB
        /// </summary>
        /// <param name="C">0-1</param>
        /// <param name="M">0-1</param>
        /// <param name="Y">0-1</param>
        /// <returns></returns>
        public static byte[] CMY2RGB(double C, double M, double Y)
        {
            return new byte[3] {
                (byte)(255 - C*255.0),
                (byte)(255 - M*255.0),
                (byte)(255 - Y*255.0)
            };
        }
        public static byte[] CMY2RGB(double[] CMY)
        {
            return CMY2RGB(CMY[0], CMY[1], CMY[2]);
        }
        #endregion

        #region RGB <-> CMYK
        /// <summary>
        /// RGB 轉 CMYK(CMJN)
        /// CMYK表示青(Cyan)品紅(Magenta)黃(Yellow)黑(BlacK)四種顏料,
        /// https://blog.csdn.net/rentian1/article/details/81185515
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>double[4]{C,M,Y,K}:(0-1.0)</returns>
        public static double[] RGB2CMYK(byte R, byte G, byte B)
        {
#if __ORIGINAL_SOURSECODE__
            double C = 1.0 - R / 255.0;
            double M = 1.0 - G / 255.0;
            double Y = 1.0 - B / 255.0;
            double K = Math.Min(C, Math.Min(M, Y));
            if (Math.Abs(K - 1.0) < float.Epsilon)
            {
                return new double[4] { 0.0, 0.0, 0.0, K };
            }
            C = (C - K) / (1.0 - K);
            M = (M - K) / (1.0 - K);
            Y = (Y - K) / (1.0 - K);
            return new double[4] { C, M, Y, K };
#else
            double V = 0.0039215686274;
            double C = 1.0 - R * V;
            double M = 1.0 - G * V;
            double Y = 1.0 - B * V;
            double K = Math.Min(C, Math.Min(M, Y));
            if (Math.Abs(K - 1.0) < float.Epsilon)
            {
                return new double[4] { 0.0, 0.0, 0.0, K };
            }
            double Z = 1.0 / (1.0 - K);
            C = (C - K) * Z;
            M = (M - K) * Z;
            Y = (Y - K) * Z;
            return new double[4] { C, M, Y, K };
#endif
        }
        public static double[] RGB2CMYK(byte[] RGB)
        {
            return RGB2CMYK(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// CMYK(CMJN) 轉 RGB
        /// CMYK表示青(Cyan)品紅(Magenta)黃(Yellow)黑(BlacK)四種顏料,
        /// https://blog.csdn.net/rentian1/article/details/81185515
        /// </summary>
        /// <param name="C">Cyan(0.0-1.0)</param>
        /// <param name="M">Magenta(0.0-1.0)</param>
        /// <param name="Y">Yellow(0.0-1.0)</param>
        /// <param name="K">BlacK(0.0-1.0)</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] CMYK2RGB(double C, double M, double Y, double K)
        {
            int R = (int)(255.0 * (1.0 - C) * (1.0 - K));
            int G = (int)(255.0 * (1.0 - M) * (1.0 - K));
            int B = (int)(255.0 * (1.0 - Y) * (1.0 - K));
            return new byte[3] { Clamp(R), Clamp(G), Clamp(B) };
        }
        public static byte[] CMYK2RGB(double[] CMYK)
        {
            return CMYK2RGB(CMYK[0], CMYK[1], CMYK[2], CMYK[3]);
        }
        #endregion

        #region RGB <-> CIE LAB
        /// <summary>
        /// RGB 轉 CIE L*A*B*
        /// https://blog.csdn.net/qq_38701868/article/details/89433038
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>double[3]{L:0-100,A:-128~127,B:-128~127}</returns>
        public static double[] RGB2LAB(byte R, byte G, byte B)
        {
            double L = 0.3811 * R + 0.5783 * G + 0.0402 * B;
            double M = 0.1967 * R + 0.7244 * G + 0.0782 * B;
            double S = 0.0241 * R + 0.1288 * G + 0.8444 * B;

            //若RGB值均為0,則LMS為0,防止數學錯誤log0
            if ((int)L != 0) L = Math.Log(L) / Math.Log(10.0);
            if ((int)M != 0) M = Math.Log(M) / Math.Log(10.0);
            if ((int)S != 0) S = Math.Log(S) / Math.Log(10.0);

            double aL = (L + M + S) / Math.Sqrt(3.0);
            double aA = (L + M - 2 * S) / Math.Sqrt(6.0);
            double aB = (L - M) / Math.Sqrt(2.0);
            return new double[3] { aL, aA, aB };
        }
        public static double[] RGB2LAB(byte[] RGB)
        {
            return RGB2LAB(RGB[0], RGB[1], RGB[2]);
        }

        /// <summary>
        /// CIE LAB 轉 RGB
        /// https://blog.csdn.net/qq_38701868/article/details/89433038
        /// </summary>
        /// <param name="L">(0-100)</param>
        /// <param name="A">(-128~127)</param>
        /// <param name="B">(-128~127param>
        /// <returns>byte[3]{r,g,b}</returns>
        public static byte[] LAB2RGB(double L, double A, double B)
        {
            L /= Math.Sqrt(3.0);
            A /= Math.Sqrt(6.0);
            B /= Math.Sqrt(2.0);

            double xL = L + A + B;
            double xM = L + A - B;
            double xS = L - 2 * A;

            xL = Math.Pow(10.0, xL);
            xM = Math.Pow(10.0, xM);
            xS = Math.Pow(10.0, xS);

            double dR = +4.4679 * xL - 3.5873 * xM + 0.1193 * xS;
            double dG = -1.2186 * xL + 2.3809 * xM - 0.1624 * xS;
            double dB = +0.0497 * xL - 0.2439 * xM + 1.2045 * xS;

            //防止溢位,若求得RGB值大于255則置為255,若小于0則置為0
            return new byte[3] { Clamp((int)dR), Clamp((int)dG), Clamp((int)dB) };
        }
        public static byte[] LAB2RGB(double[] LAB)
        {
            return LAB2RGB(LAB[0], LAB[1], LAB[2]);
        }
        #endregion

        #region RGB <-> CIE 1931 XYZ
#if __OTHER__
        /// <summary>
        /// RGB 轉 CIE XYZ
        /// https://www.cnblogs.com/Imageshop/archive/2013/01/31/2888097.html
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>double[3]{X,Y,Z}</returns>
        public static double[] RGB2XYZ(byte R, byte G, byte B)
        {
            double X = 0.412453 * R + 0.357580 * G + 0.180423 * B;
            double Y = 0.212671 * R + 0.715160 * G + 0.072169 * B;
            double Z = 0.019334 * R + 0.119193 * G + 0.950227 * B;

            //laviewpbt修正后的公式
            //double X = 0.433953 * R + 0.376219 * G + 0.189828 * B;
            //double Y = 0.212671 * R + 0.715160 * G + 0.072169 * B;
            //double Z = 0.017758 * R + 0.109477 * G + 0.872765 * B;
            return new double[3] { X / 255.0, Y / 255.0, Z / 255.0 };
        }
#endif

        /// <summary>
        /// RGB 轉 CIE 1931/1976 XYZ
        /// (Observer = 2°, Illuminant = D65)
        /// https://www.cnblogs.com/Free-Thinker/p/4950364.html
        /// https://blog.csdn.net/timini/article/details/78949768
        /// </summary>
        /// <param name="R">(0-255)</param>
        /// <param name="G">(0-255)</param>
        /// <param name="B">(0-255)</param>
        /// <returns>double[3]{X:0-0.9505,Y:0-1.0000,Z:0-1.0890}</returns>
        public static double[] RGB2XYZ(byte R, byte G, byte B)
        {
            // Normalize red, green, blue values 
            double r = R / 255.0;
            double g = G / 255.0;
            double b = B / 255.0;

            // convert to a sRGB form 
            // 按https://www.cnblogs.com/Free-Thinker/p/4950364.html 2.2
            // 按https://github.com/hvalidi/ColorMine 2.4
            // CIE 1931 XYZ:2.2 --> 2.4
            double X = (r > 0.04045) ? Math.Pow((r + 0.055) / (1.055), 2.4) : (r / 12.92);
            double Y = (g > 0.04045) ? Math.Pow((g + 0.055) / (1.055), 2.4) : (g / 12.92);
            double Z = (b > 0.04045) ? Math.Pow((b + 0.055) / (1.055), 2.4) : (b / 12.92);
            // converts 
#if __ORIGINAL_SOURCECODE__
            return new double[3] {
                (0.4124 * X + 0.3576 * Y + 0.1805 * Z),
                (0.2126 * X + 0.7152 * Y + 0.0722 * Z),
                (0.0193 * X + 0.1192 * Y + 0.9505 * Z)
            };
#endif
            return new double[3] {
                (0.4124564 * X + 0.3575761 * Y + 0.1804375 * Z),
                (0.2126729 * X + 0.7151522 * Y + 0.0722750 * Z),
                (0.0193339 * X + 0.1191920 * Y + 0.9503041 * Z)
            };
        }

        public static double[] RGB2XYZ(byte[] RGB)
        {
            return RGB2XYZ(RGB[0], RGB[1], RGB[2]);
        }

#if __OTHER__
        /// <summary>
        /// CIE 1931 XYZ 轉 RGB
        /// https://www.cnblogs.com/Imageshop/archive/2013/01/31/2888097.html
        /// </summary>
        /// <param name="X">(0-255)</param>
        /// <param name="Y">(0-255)</param>
        /// <param name="Z">(0-255)</param>
        /// <returns>byte[3]{R,G,B}</returns>
        public static byte[] XYZ2RGB(double X, double Y, double Z)
        {
            double R = +3.240479 * X - 1.537150 * Y - 0.498535 * Z; R *= 255.0;
            double G = -0.969256 * X + 1.875992 * Y + 0.041556 * Z; G *= 255.0;
            double B = +0.055648 * X - 0.204043 * Y + 1.057311 * Z; B *= 255.0;

            //laviewpbt修正后的公式
            //double R = +3.0799327 * X - 1.537150 * Y - 0.5427820 * Z; R *= 255.0;
            //double G = -0.9212350 * X + 1.875992 * Y + 0.0452442 * Z; G *= 255.0;
            //double B = +0.0528909 * X - 0.204043 * Y + 1.1511515 * Z; B *= 255.0;
            return new byte[3] { Clamp((int)R), Clamp((int)G), Clamp((int)B) };
        }
#endif

        /// <summary>
        /// CIE 1931/1976 XYZ 轉 RGB
        /// (Observer = 2°, Illuminant = D65)
        /// https://www.cnblogs.com/Free-Thinker/p/4950364.html
        /// https://blog.csdn.net/timini/article/details/78949768
        /// </summary>
        /// <param name="X">0-0.9505</param>
        /// <param name="Y">0-1.0000</param>
        /// <param name="Z">0-1.0890</param>
        /// <returns>byte[3]{R,G,B]</returns>
        public static byte[] XYZ2RGB(double X, double Y, double Z)
        {
#if __ORIGINAL_SOURCECODE__
            double[] c = new double[3];
            c[0] = +X * 3.2410 - Y * 1.5374 - Z * 0.4986; // red 
            // G -0.0416 WRONG!應該是 +0.415
            c[1] = -X * 0.9692 + Y * 1.8760 - Z * 0.0416; // green
            c[2] = +X * 0.0556 - Y * 0.2040 + Z * 1.0570; // blue 
            for (int i = 0; i < 3; i++)
            {
                c[i] = (c[i] <= 0.0031308) ? 12.92 * c[i] : (1 + 0.055) * Math.Pow(c[i], (1.0 / 2.4)) - 0.055;
                c[i] *= 255.0;
            }
            return new byte[3] {
                Clamp((int)c[0]),
                Clamp((int)c[1]),
                Clamp((int)c[2])
            };
#endif
            // https://www.cnblogs.com/Free-Thinker/p/4950364.html
            //double R = +X * 3.2410 - Y * 1.5374 - Z * 0.4986;
            // 這行系數不同 -0.0416 ? +0.415
            //double G = -X * 0.9692 + Y * 1.8760 - Z * 0.0416;
            //double B = +X * 0.0556 - Y * 0.2040 + Z * 1.0570;

            // https://github.com/hvalidi/ColorMine
            double R = +3.2406 * X - 1.5372 * Y - 0.4986 * Z;
            double G = -0.9689 * X + 1.8758 * Y + 0.0415 * Z;
            double B = +0.0557 * X - 0.2040 * Y + 1.0570 * Z;
            R = (R <= 0.0031308) ? 12.92 * R : 1.055 * Math.Pow(R, (1.0 / 2.4)) - 0.055;
            G = (G <= 0.0031308) ? 12.92 * G : 1.055 * Math.Pow(G, (1.0 / 2.4)) - 0.055;
            B = (B <= 0.0031308) ? 12.92 * B : 1.055 * Math.Pow(B, (1.0 / 2.4)) - 0.055;
            R *= 255.0; G *= 255.0; B *= 255.0;
            return new byte[3] { Clamp((int)R), Clamp((int)G), Clamp((int)B) };
        }

        public static byte[] XYZ2RGB(double[] XYZ)
        {
            return XYZ2RGB(XYZ[0], XYZ[1], XYZ[2]);
        }
        #endregion

        #region XYZ <-> LAB

        /// <summary>
        /// CIE 1931 XYZ 轉 CIE LAB
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/02/2889897.html
        /// </summary>
        /// <param name="X">(0-0.9505)</param>
        /// <param name="Y">(0-1)</param>
        /// <param name="Z">(0-1.089)</param>
        /// <returns>byte[3]{L:0-100,A:-128~127,B:-128&127}</returns>
        public static double[] XYZ2LAB(double X, double Y, double Z)
        {
            double L = 116.0 * LABFunction(Y) - 16.0;
            double A = 500.0 * (LABFunction(X) - LABFunction(Y));
            double B = 200.0 * (LABFunction(Y) - LABFunction(Z));
            return new double[3] { L, A, B };
        }
        public static double[] XYZ2LAB(double[] XYZ)
        {
            return XYZ2LAB(XYZ[0], XYZ[1], XYZ[2]);
        }
        private static double LABFunction(double t)
        {
#if __ORIGINAL_SOURSECODE__
            if (t > Math.Pow(6.0 / 29.0, 3.0)) return Math.Pow(t, 1.0 / 3.0);
            return (Math.Pow(29.0 / 6.0, 2.0) * t / 3.0 + 4.0 / 29.0);
#else
            return ((t > 0.008856) ? Math.Pow(t, (1.0 / 3.0)) : (7.787 * t + 16.0 / 116.0));
#endif
        }

        /// <summary>
        /// CIE LAB 轉 CIE 1931 XYZ
        /// https://www.cnblogs.com/Imageshop/archive/2013/02/02/2889897.html
        /// https://www.cnblogs.com/Free-Thinker/p/4950364.html
        /// </summary>
        /// <param name="L">0-100</param>
        /// <param name="A">-128~127</param>
        /// <param name="B">-128~127</param>
        /// <returns>double[3]{X:0-0.9505,Y:0-1,Z:0-1.089}</returns>
        public static double[] LAB2XYZ(double L, double A, double B)
        {
            double Y = LABFunctionRev((L + 16.0) / 116.0);
            double X = LABFunctionRev((L + 16.0) / 116.0 + A / 500.0);
            double Z = LABFunctionRev((L + 16.0) / 116.0 + B / 200.0);
            return new double[3] { X, Y, Z };
        }
        public static double[] LAB2XYZ(double[] LAB)
        {
            return LAB2XYZ(LAB[0], LAB[1], LAB[2]);
        }
        private static double LABFunctionRev(double t)
        {
            if (t > (6.0 / 29.0)) return Math.Pow(t, 3.0);
            return (3.0 * Math.Pow(6.0 / 29.0, 2.0) * (t - 4.0 / 29.0));
        }
        #endregion

        #region HSB,HSL,CMYK,YUV,...

        /// <summary>
        /// HSB 轉 HSL
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1</param>
        /// <param name="B">0-1</param>
        /// <returns>double[3]{H:0-360,S:0-1,L:0-1}</returns>
        public static double[] HSB2HSL(double H, double S, double B)
        {
            return RGB2HSL(HSB2RGB(H, S, B));
        }

        /// <summary>
        /// HSL 轉 HSB
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1.0</param>
        /// <param name="L">0-1.0</param>
        /// <returns>double[3]{H:0-360,S:0-1,B:0-1}</returns>
        public static double[] HSL2HSB(double H, double S, double L)
        {
            return RGB2HSB(HSL2RGB(H, S, L));
        }

        /// <summary>
        /// HSL 轉 CMYK
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1.0</param>
        /// <param name="L">0-1.0</param>
        /// <returns></returns>
        public static double[] HSL2CMYK(double H, double S, double L)
        {
            return RGB2CMYK(HSL2RGB(H, S, L));
        }

        /// <summary>
        /// HSL 轉 YUV
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1.0</param>
        /// <param name="L">0-1.0</param>
        /// <returns>double[3](Y:0-1,U:-0.436~0.436,V:-0.615~0.615)</returns>
        public static double[] HSL2YUV(double H, double S, double L)
        {
            return RGB2YUV(HSL2RGB(H, S, L));
        }

        /// <summary>
        /// HSB 轉 CMYK
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1</param>
        /// <param name="B">0-1</param>
        /// <returns>double[4]{C,M,Y,K}(0-1)</returns>
        public static double[] HSB2CMYK(double H, double S, double B)
        {
            return RGB2CMYK(HSB2RGB(H, S, B));
        }

        /// <summary>
        /// HSB 轉 YUV
        /// </summary>
        /// <param name="H">0-360</param>
        /// <param name="S">0-1</param>
        /// <param name="B">0-1</param>
        /// <returns>double[3](Y:0-1,U:-0.436~0.436,V:-0.615~0.615)</returns>
        public static double[] HSB2YUV(double H, double S, double B)
        {
            return RGB2YUV(HSB2RGB(H, S, B));
        }

        /// <summary>
        /// CMYK 轉 HSL
        /// </summary>
        /// <param name="C">Cyan(0.0-1.0)</param>
        /// <param name="M">Magenta(0.0-1.0)</param>
        /// <param name="Y">Yellow(0.0-1.0)</param>
        /// <param name="K">BlacK(0.0-1.0)</param>
        /// <returns>double[3]{H:0-360,S:0-1,L:0-1}</returns>
        public static double[] CMYK2HSL(double C, double M, double Y, double K)
        {
            return RGB2HSL(CMYK2RGB(C, M, Y, K));
        }

        /// <summary>
        /// CMYK 轉 HSB
        /// </summary>
        /// <param name="C">Cyan(0.0-1.0)</param>
        /// <param name="M">Magenta(0.0-1.0)</param>
        /// <param name="Y">Yellow(0.0-1.0)</param>
        /// <param name="K">BlacK(0.0-1.0)</param>
        /// <returns>double[3]{H:0-360,S:0-1,B:0-1}</returns>
        public static double[] CMYK2HSB(double C, double M, double Y, double K)
        {
            return RGB2HSB(CMYK2RGB(C, M, Y, K));
        }

        /// <summary>
        /// CMYK 轉 YUV
        /// </summary>
        /// <param name="C">Cyan(0.0-1.0)</param>
        /// <param name="M">Magenta(0.0-1.0)</param>
        /// <param name="Y">Yellow(0.0-1.0)</param>
        /// <param name="K">BlacK(0.0-1.0)</param>
        /// <returns>double[3](Y:0-1,U:-0.436~0.436,V:-0.615~0.615)</returns>
        public static double[] CMYK2YUV(double C, double M, double Y, double K)
        {
            return RGB2YUV(CMYK2RGB(C, M, Y, K));
        }

        /// <summary>
        /// YUV 轉 HSL
        /// </summary>
        /// <param name="Y">byte(0-255)</param>
        /// <param name="U">byte(0-255),實際數值范圍沒有這么大,</param>
        /// <param name="V">byte(0-255),實際數值范圍沒有這么大,</param>
        /// <returns>double[3]{H:0-360,S:0-1,L:0-1}</returns>
        public static double[] YUV2HSL(byte Y, byte U, byte V)
        {
            return RGB2HSL(YUV2RGB(Y, U, V));
        }

        /// <summary>
        /// YUV 轉 HSL
        /// </summary>
        /// <param name="Y">(0,1.0)</param>
        /// <param name="U">(-0.436,+0.436)</param>
        /// <param name="V">(-0.615,+0.615)</param>
        /// <returns>double[3]{H:0-360,S:0-1,L:0-1}</returns>
        public static double[] YUV2HSL(double Y, double U, double V)
        {
            return RGB2HSL(YUV2RGB(Y, U, V));
        }

        /// <summary>
        /// YUV 轉 HSB
        /// </summary>
        /// <param name="Y">(0,1.0)</param>
        /// <param name="U">(-0.436,+0.436)</param>
        /// <param name="V">(-0.615,+0.615)</param>
        /// <returns>double[3]{H:0-360,S:0-1,B:0-1}</returns>
        public static double[] YUV2HSB(double Y, double U, double V)
        {
            return RGB2HSB(YUV2RGB(Y, U, V));
        }

        /// <summary>
        /// YUV 轉 CMYK
        /// </summary>
        /// <param name="Y">(0,1.0)</param>
        /// <param name="U">(-0.436,+0.436)</param>
        /// <param name="V">(-0.615,+0.615)</param>
        /// <returns>double[4]{C,M,Y,K}(0-1)</returns>
        public static double[] YUV2CMYK(double Y, double U, double V)
        {
            return RGB2CMYK(YUV2RGB(Y, U, V));
        }
        #endregion

    }

    #region 測驗代碼
    public static class ColorspaceTest
    {
        /// <summary>
        /// 生成典型顏色值的換算 與 逆換算 的結果對照 html
        /// Usage: 
        /// (1)輸出為檔案
        /// File.WriteAllText("colorspace.html", ColorspaceTest.Execute(), Encoding.UTF8);
        /// (2)直接顯示
        /// webBrowser.DocumentText = ColorspaceTest.Execute();
        /// </summary>
        /// <returns></returns>
        public static string Execute()
        {
            Color[] colors = new Color[7] {
                Color.Cyan,Color.Black, Color.White,Color.Gray,
                Color.FromArgb(255,0,0),Color.FromArgb(0,255,0),
                Color.FromArgb(0,0,255)
            };
            // 第一個顏色為隨機顏色
            Random rnd = new Random((int)DateTime.Now.Ticks);
            colors[0] = Color.FromArgb((byte)rnd.Next(255), (byte)rnd.Next(255), (byte)rnd.Next(255));

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<style>");
            sb.AppendLine("table { border-collapse:collapse; } ");
            sb.AppendLine("tr { height:35px;} ");
            sb.AppendLine("td { padding:5px;text-align:center;} ");
            sb.AppendLine("</style>");
            sb.AppendLine("<table border=1>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>CMYK</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2CMYK(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.CMYK2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>HSB</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2HSB(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.HSB2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>HSV</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2HSV(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.HSV2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>HSL</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2HSL(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.HSL2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>HSI</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2HSI(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.HSI2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>XYZ</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2XYZ(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.XYZ2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>LAB</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2LAB(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.LAB2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>YUV</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2YUV(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.YUV2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>YCbCr</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                byte[] v = ColorspaceHelper.RGB2YCbCr(rgb);
                sb.AppendLine(Bi(v) + "<br>");
                rgb = ColorspaceHelper.YCbCr2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("<tr>");
            sb.AppendLine("<td>YDbDr</td>");
            foreach (Color c in colors)
            {
                sb.AppendLine("<td>");
                byte[] rgb = ColorspaceHelper.FromColor(c);
                sb.AppendLine(Bi(rgb) + "<br>");
                double[] v = ColorspaceHelper.RGB2YDbDr(rgb);
                sb.AppendLine(Fi(v) + "<br>");
                rgb = ColorspaceHelper.YDbDr2RGB(v);
                sb.AppendLine(Bi(rgb) + "<br>");
                sb.AppendLine("</td>");
            }
            sb.AppendLine("</tr>");

            sb.AppendLine("</table>");
            return sb.ToString();
        }

        private static string Bi(byte[] v)
        {
            return v[0] + "," + v[1] + "," + v[2];
        }

        private static string Fi(double[] v)
        {
            string s = "";
            for (int i = 0; i < v.GetLength(0); i++)
            {
                s += String.Format("{0:F4}", v[i]) + ",";
            }
            return s.Substring(0, s.Length - 1);
        }
    }
    #endregion
}

歡迎討論指正,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/261462.html

標籤:其他

上一篇:B - Eastern Exhibition(思維)

下一篇:c語言中的字串函式的模擬實作

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more