Source: Imagelib.js

//ImageLibJS是一个基于ES6面向WEB的以图像处理为核心的数组运算库。
//文档生成命令:npx jsdoc ./Imagelib.js -d docs

/**
 * @class ImgArray更重要的是数组,提供数组的计算,但是数组的运算支持上偏向于图像处理,
 * 后期可以仿照Numpy转为强大的数组计算库,支持常见的多种元素数据类型初始化,但是仅对
 * float32类型进行了广泛地验证和测试,其他类型未验证,使用时需要注意自行检查。
 */
class ImgArray {
    /**
     * dtypes 是一个静态对象,对于存储数组数据的类型与JS TypedArray的映射关系。
     * @type {Object}
     * @example 
     *   //dtypes的值如下:
     *   dtypes = {
     *   float32: Float32Array,
     *   uint8: Uint8Array,
     *   uint16: Uint16Array,
     *   int32: Int32Array,
     *   uint32: Uint32Array,
     *   cuint8: Uint8ClampedArray,
     *   int16: Int16Array,
     *   int8: Int8Array,
     *   float64: Float64Array,
     *   };  
    */
    static dtypes = {
        float32: Float32Array,
        uint8: Uint8Array,
        uint16: Uint16Array,
        int32: Int32Array,
        uint32: Uint32Array,
        cuint8: Uint8ClampedArray,
        int16: Int16Array,
        int8: Int8Array,
        float64: Float64Array,
    };

    /**
     * dtypenames 获取{@link ImgArray#dtypes}对象中所有数据类型的名称。返回一个包含所有数据类型名称的数组,数组中包含{@link ImgArray#dtypes}对象的所有键。
     * @static
     * @type {string[]}
     */
    static get dtypenames() {
        return Object.keys(this.dtypes);
    }

    /**
     * constructor,数组初始化函数,采用对象的方式进行初始化,提供了默认参数。
     * 只实现了三维数组,各维度类比于图像的高,宽和通道,即height,width,channel。
     * @constructor
     * @param {Object} options - 配置对象,用于初始化图像数据。
     * @param {number} [options.height=256] - 表示数组(图像)的高度,默认值是256
     * @param {number} [options.width=256] - 表示数组(图像)的宽度,默认值是256
     * @param {number} [options.channel=3] - 表示数组(图像)的通道数,默认值是3
     * @param {boolean} [options.lazy=false] - 默认值为false,表示进行数组元素的初始化,否则不进行元素的数组元素的初始化
     * @param {string} [options.dtype='float32'] - 默认为'float32',可选的值为{@link ImgArray.dtypenames}中的值  
     * @example
     * let imarr=new ImgArray() //使用默认参数创建一个256*256*3的float32类型的数组,数组元素初始值为0。
     * let imarra=new ImgArray({height:512,width:512,channel=1,type='uint8'}) //创建一个512*512*1的uint8类型的数组,数组元素实始值为0。
     * let imarrb=new ImgArray({height:512,width:512,channel=1,type='uint8',lazy:true}) //创建一个512*512*1的uint8类型的数组,数组元素不进行初始化。
     */
    constructor({ height = 256, width = 256, channel = 3, lazy = false, dtype = 'float32' } = {}) {
        this.height = height;
        this.width = width;
        this.channel = channel;
        this.numel = height * width * channel;  //元素数量
        this.dtype = dtype
        this.data = null;
        if (lazy == false) {
            this.initarray()
        }
    }

    /**
     * initarray() 根据数组的基本信息,为数组分配存储空间。
     * 当数组初始化时lazy=false时调用,分配空间,分配后数组元素的值为0。
     *  @returns {ImgArray} 返回当前实例,以支持链式调用。
     */
    initarray() {
        if (this.data == null) {
            this.data = new ImgArray.dtypes[this.dtype](this.numel);
        }
        this.lazy = false;
        return this;
    }


    /**
     * 表示数组形状
     * @typedef {Object} Shape 
     * @property {number} height - 数组的高度
     * @property {number} width  - 数组的宽度
     * @property {number} channel -数组的通道数
     */

    /** 
     * 返回图像的形状对象。用属性的方式获取数组的形状,包括高,宽,通道。
     * @type {Shape}
     */
    get shape() {
        return { height: this.height, width: this.width, channel: this.channel };
    }

    /**
     * size,获取数组元素的个数。
     * @type {number}
     */
    get size() {
        return this.numel;
    }

    /**
     * elbytes,数组中一个元素占用的字节数。
     * @type {number} 
     */
    get elbytes() {
        return ImgArray.dtypes[this.dtype].BYTES_PER_ELEMENT;
    }

    /**
     * bytesize,用属性的方式获取数组占用的总字节数。
     * @type {number} 
     */
    get bytesize() {
        return this.numel * this.elbytes;
    }

    /**
     * elidxtoidx,将元素的索引转为数组的三个索引值[h,w,c]
     * 该函数是一个内部函数,一般由其他方法调用,不对外使用
     * @param {number} [elidx=0] - 线性索引,默认值为0,表示获取第一个元素,即[0,0,0]
     * @returns {number[]|undefined} 返回一个包含三维索引的数组,或者如果 `elidx` 超出范围,返回 `undefined`。
     *  - hidx: 高度索引,表示数组的第0维索引。
     *  - widx: 宽度索引,表示数组的第1维索引。
     *  - cidx: 通道索引,表示数组的第2维索引。
     */
    elidxtoidx(elidx = 0) {
        if (elidx < 0 || elidx > this.numel - 1) return;
        let hwidth = this.width * this.channel;
        let hidx = parseInt(elidx / hwidth);
        elidx = elidx % hwidth;
        let widx = parseInt(elidx / this.channel);
        let cidx = elidx % this.channel;
        return [hidx, widx, cidx];
    }

    /**
     * idxtoelidx,将数组的三个索引值[h,w,c],转为元素索引elidx
     * 该函数是一个内部函数,一般由其他方法调用,不对外使用
     * @param {number} hidx - 数组元素的高索引,也就是第0维度的索引
     * @param {number} widx - 数组元素的宽索引,也就是第1维度的索引
     * @param {number} cidx - 数组元素的通道索引,也就是第2维度的索引
     * @return {number} - 元素在数组内的真实索引值
     */
    idxtoelidx(hidx, widx, cidx) {
        return (hidx * this.width + widx) * this.channel + cidx;
    }

    /**
     * checkisvalid,检查索引值是否合法,即是否在数组范围内
     * 目前只支持正索引
     * @param {number} hidx - 数组元素的高索引,也就是第0维度的索引
     * @param {number} widx - 数组元素的宽索引,也就是第1维度的索引
     * @param {number} cidx - 数组元素的通道索引,也就是第2维度的索引
     * @return {boolean} - true表示索引合法,false表示索引不合法
     */
    checkisvalid(hidx, widx, cidx) {
        if (hidx + 1 > this.height || hidx < 0) return false;
        if (widx + 1 > this.width || widx < 0) return false;
        if (cidx + 1 > this.channel || cidx < 0) return false;
        return true;
    }

    /**
     * getel,根据数组索引获取元素值
     * @param {number} hidx - 数组元素的高索引,也就是第0维度的索引 
     * @param {number} widx - 数组元素的宽索引,也就是第1维度的索引
     * @param {number} cidx - 数组元素的通道索引,也就是第2维度的索引
     * @returns {number|null} 元素值,若数组元素索引越界,则返回null
     */
    getel(hidx, widx, cidx) {
        //由数组索引获取元素值
        // TODO: 当数组为
        if (this.data === null) {
            console.error('数组没有初始化');
            return null;
        }
        if (this.checkisvalid(hidx, widx, cidx)) {
            return this.data.at(this.idxtoelidx(hidx, widx, cidx));
        }
        console.error('数组元素索引越界');
        return null;
    }

    /**
     * setel,根据索引设置指定元素的值
     * @param {number} hidx - 数组元素的高索引,也就是第0维度的索引 
     * @param {number} widx - 数组元素的宽索引,也就是第1维度的索引
     * @param {number} cidx - 数组元素的通道索引,也就是第2维度的索引
     * @param {number} value - 要设置的元素值
     * @returns {ImgArray|null} 该数组对象本身,若数组元素索引越界,则返回null
     */
    setel(hidx, widx, cidx, value) {
        this.initarray();
        if (this.checkisvalid(hidx, widx, cidx)) {
            this.data[this.idxtoelidx(hidx, widx, cidx)] = value;
            return this;  //以供链式调用
        }
        console.error('数组元素索引越界')
        return null;
    }

    /**
     * fill,向数组填充常数数值。
     * @param {number|number[]} [value=3] - 要填充的值,可以是一个数值,也可以是一个数组,数组长度与通道数相同,表示对每个通道进行填充
     * @param {number|number[]} [cidx=null] - 要填充的通道索引,可以是一个数值,也可以是一个数组,表示对多个通道进行填充
     * @returns {ImgArray} 该数组对象本身
     * @example 
     *      new ImgArray().fill(255);        //填充所有元素值为255;
     * @example
     *      new ImgArray().fill(255,1);     //只对1通道填充为255;
     * @example
     *      new ImgArray().fill(255,[0,1]); //对0,1通道填充均填充为255
     * @example
     *      new ImgArray().fill([255,120],[0,2]);    //对0通道填充255,对2道填充120
     */
    fill(value = 3, cidx = null) {
        this.initarray();
        if (cidx == null) {
            this.data.fill(value);
            return this;
        }
        if (cidx instanceof Array) {
            if (value instanceof Array) {
                if (value.length == cidx.length) {
                    let map = {};
                    cidx.forEach((x, idx) => { map[x] = value[idx]; });
                    this.data.forEach((x, idx, arr) => {
                        let ci = idx % this.channel;
                        if (map[ci]) arr[idx] = map[ci];
                    }, this);
                }
                else {
                    console.error("value数组长度与cidx数组长度不一致");
                }
            }
            else {
                this.data.forEach((x, idx, arr) => { if (cidx.includes(idx % this.channel)) arr[idx] = value; }, this);
            }
        }
        else {
            this.data.forEach((x, idx, arr) => { if (idx % this.channel == cidx) arr[idx] = value; }, this);
        }
        return this;
    }

    /**
     * issameshape,两数组形状是否相同
     * @param {ImgArray} bimgarr - 另一个数组
     * @returns {Boolean} 两数组形状是否相同
     */
    issameshape(bimgarr) {
        //判断两数组形状是否相同
        return this.height === bimgarr.height &&
            this.width === bimgarr.width &&
            this.channel === bimgarr.channel;
    }

    /**
     * issamehw,两数组宽高是否相同
     * @param {ImgArray} bimgarr - 另一个数组
     * @returns {Boolean} 两数组宽高是否相同
     */
    issamehw(bimgarr) {
        return this.height === bimgarr.height && this.width === bimgarr.width;
    }

    /**
     * issamech,判断两数组的通道是否相同
     * @param {ImgArray} bimgarr - 另一个数组
     * @returns {Boolean} 两数组的通道是否相同
     */
    issamech(bimgarr) {
        return this.channel === bimgarr.channel
    }

    /**
     * dstack,将多个数组沿通道堆叠,数组的宽高必须要相同
     * @param {ImgArray|...ImgArray} - 一个或多个数组 
     * @returns {ImgArray|undefined} 堆叠形成的新数组,若堆叠数组的宽高尺寸不一致,返回undefined
     */
    dstack(...imgars) {
        //imgars里为ImgArray实例,应当要具备相同的尺寸
        //将该数组与另外多个高宽一致的数组堆叠为一个新数组
        let tmpimgarrs = [this, ...imgars];
        let channelnum = 0;
        let channelmapping = [];
        for (let imgidx = 0; imgidx < tmpimgarrs.length; imgidx++) {
            if (!this.issamehw(tmpimgarrs[imgidx])) {
                console.error(`错误:第${imgidx}个堆叠数组的宽高尺寸不一致`);
                return;
            }
            let tmpchannel = tmpimgarrs[imgidx].channel;
            channelnum += tmpchannel;
            for (let cidx = 0; cidx < tmpchannel; cidx++) {
                channelmapping.push([imgidx, cidx]);
            }
        }
        let newimgarr = new ImgArray({ height: this.height, width: this.width, channel: channelnum })

        for (let hidx = 0; hidx < newimgarr.height; hidx++) {
            for (let widx = 0; widx < newimgarr.width; widx++) {
                for (let cidx = 0; cidx < newimgarr.channel; cidx++) {
                    let [imgidx, ocidx] = channelmapping[cidx];
                    let tmpv = tmpimgarrs[imgidx].getel(hidx, widx, ocidx);
                    newimgarr.setel(hidx, widx, cidx, tmpv)
                }
            }
        }
        return newimgarr;
    }

    /**
     * dsplit,提取数组的某些通道
     * @param {...number} channels - 要提取的通道索引
     * @returns {ImgArray} 提取的数组
     */
    dsplit(...channels) {
        //将数组沿通道进行分割
        //可获取指定通道的数据
        //dsplit(0,2),获取第0和2通道,返回是一个ImgArray的数组
        let arr = [];
        if (channels.length == 0) channels = [...new Array(this.channel).keys()]
        for (let i of channels) {
            let tmp = new ImgArray({ height: this.height, width: this.width, channel: 1 });
            for (let hidx = 0; hidx < this.height; hidx++) {
                for (let widx = 0; widx < this.width; widx++) {
                    let value = this.getel(hidx, widx, i);
                    tmp.setel(hidx, widx, 0, value);
                }
            }
            arr.push(tmp);
        }
        return arr;
    }

    /**
     * hstack,水平堆叠数组,沿宽度方向叠加
     * @param {...ImgArray} imgars - 水平堆叠的数组
     * @returns {ImgArray} 水平堆叠后的结果
     */
    hstack(...imgars) {
        //需要数组的高和通道数一置,但是该函数不进行检测,由用户来保证
        let tmpimgarrs = [this, ...imgars];
        let newwidth = 0;
        let widths = [];
        for (let imgidx = 0; imgidx < tmpimgarrs.length; imgidx++) {
            let tmpwidth = tmpimgarrs[imgidx].width;
            newwidth += tmpwidth;
            widths.push(newwidth);
        }
        let newimgarr = new ImgArray({ height: this.height, width: newwidth, channel: this.channel })

        for (let hidx = 0; hidx < newimgarr.height; hidx++) {
            for (let widx = 0; widx < newimgarr.width; widx++) {
                let imgidx = widths.map(x => widx < x).indexOf(true);
                let owidx = imgidx > 0 ? widx - widths[imgidx - 1] : widx;
                for (let cidx = 0; cidx < newimgarr.channel; cidx++) {
                    newimgarr.setel(hidx, widx, cidx, tmpimgarrs[imgidx].getel(hidx, owidx, cidx));
                }
            }
        }
        return newimgarr;
    }

    /**
     * vstack,垂直堆叠数组,沿高度方向叠加
     * @param  {...ImgArray} imgarrs - 垂直堆叠的数组
     * @returns {ImgArray} 垂直堆叠后的结果
     */
    vstack(...imgarrs) {
        //需要数组的宽和通道数一置,但是该函数不进行检测,由用户来保证
        let tmpimgarrs = [this, ...imgarrs];
        let newheight = 0;
        for (let imgidx = 0; imgidx < tmpimgarrs.length; imgidx++) {
            newheight += tmpimgarrs[imgidx].height;
        }
        let newarray = new ImgArray({ height: newheight, width: this.width, channel: this.channel });
        let offsetidx = 0;
        for (let imgidx = 0; imgidx < tmpimgarrs.length; imgidx++) {
            newarray.data.set(tmpimgarrs[imgidx].data, offsetidx);
            offsetidx += tmpimgarrs[imgidx].numel;
        }
        return newarray;
    }

    /**
     * 最近邻放大
     * scale,数组,使用最近邻方法进行对数组在宽度和高度上进行整数倍的缩放。
     * @param {number} scale -尺寸
     */
    scale(k = 8) {
        let outarr = new ImgArray({ height: this.height * k, width: this.width * k, channel: this.channel, dtype: this.dtype })
        for (let hidx = 0; hidx < outarr.height; hidx++) {
            for (let widx = 0; widx < outarr.width; widx++) {
                for (let cidx = 0; cidx < outarr.channel; cidx++) {
                    let v = this.getel(parseInt(hidx / k), parseInt(widx / k), cidx);
                    outarr.setel(hidx, widx, cidx, v);
                }
            }
        }
        return outarr;
    }


    /**
     * grid,将多个尺寸相同的数组堆叠成网格
     * @param {number} row - 行数
     * @param {number} col - 列数
     * @param  {...ImgArray} imgarrs - 需要堆叠的数组
     * @returns {ImgArray|undefined} 网格堆叠后的结果,若尺寸不一致,返回undefined
     */
    grid(row, col, ...imgarrs) {
        //参数为ImgArray实例,且必须为相同尺寸
        let tmpimgarrs = [this, ...imgarrs];
        if (row * col < 2 && tmpimgarrs.length != row * col) {
            console.error(`错误:参数错误,row=${row},col=${col},imgarrs.length=${imgarrs.length}`);
            return;
        }
        let outarr = new ImgArray({ height: row * this.height, width: col * this.width, channel: this.channel });
        for (let hidx = 0; hidx < outarr.height; hidx++) {
            for (let widx = 0; widx < outarr.width; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    const imghidx = Math.floor(hidx / this.height);
                    const imgwidx = Math.floor(widx / this.width);
                    const imgidx = imghidx * col + imgwidx;
                    let tmpv = tmpimgarrs[imgidx].getel(hidx % this.height, widx % this.width, cidx);
                    outarr.setel(hidx, widx, cidx, tmpv);
                }
            }
        }
        return outarr;
    }

    /** 
    * repeat,在水平和竖直方向重复数组
    * @param {number} row - 在高度方向上重复的次数,也就是在行方向上重复的次数
    * @param {number} col - 在宽度方向上重复的次数,也就是在列方向上重复的次数
    * @returns {ImgArray|undefined} 重复后的结果,若row * col < 2,则返回undefined
    */
    repeat(row, col) {
        if (row * col < 2) {
            console.error(`错误:repeat参数错误,row=${row},col=${col}`);
            return;
        }
        let outarr = new ImgArray({ height: row * this.height, width: col * this.width, channel: this.channel });
        for (let hidx = 0; hidx < outarr.height; hidx++) {
            for (let widx = 0; widx < outarr.width; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let tmpv = this.getel(hidx % this.height, widx % this.width, cidx);
                    outarr.setel(hidx, widx, cidx, tmpv);
                }
            }
        }
        return outarr;
    }

    /**
     * filiplr,在宽度轴上,进行左右翻转
     * @returns {ImgArray} 水平翻转后的结果
     */
    fliplr() {
        let outarr = this.empty(false);
        for (let hidx = 0; hidx < this.height; hidx++) {
            for (let widx = 0; widx < this.width; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let tmpv = this.getel(hidx, widx, cidx);
                    outarr.setel(hidx, this.width - widx - 1, cidx, tmpv);
                }
            }
        }
        return outarr
    }

    /**
     * flipud, 在高度轴上,进行上下翻转
     * @returns {ImgArray} 上下翻转后的结果
    */
    flipud() {
        let outarr = this.empty(false);
        for (let hidx = 0; hidx < this.height; hidx++) {
            for (let widx = 0; widx < this.width; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let tmpv = this.getel(hidx, widx, cidx);
                    outarr.setel(this.height - hidx - 1, widx, cidx, tmpv)
                }
            }
        }
        return outarr
    }

    /**
     * rollud,上下滚动
     * @param {number} [dy=100] - 滚动距离,默认为100
     * @returns {ImgArray} 滚动后的结果
     */
    rollud(dy = 100) {
        dy = -Math.sign(dy) * (Math.abs(dy) % this.height);
        let newarr = this.empty(false);
        for (let hidx = 0; hidx < this.height; hidx++) {
            for (let widx = 0; widx < this.width; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let tmpv = this.getel(hidx, widx, cidx);
                    newarr.setel((hidx + dy + this.height) % this.height, widx, cidx, tmpv)
                }
            }
        }
        return newarr;
    }

    /**
     * rolllr,左右滚动
     * @param {number} [dx=100] - 滚动距离,默认为100
     * @returns {ImgArray} 滚动后的结果
     */
    rolllr(dx = 100) {
        dx = Math.sign(dx) * (Math.abs(dx) % this.width);
        let newarr = this.empty(false);
        for (let hidx = 0; hidx < this.height; hidx++) {
            for (let widx = 0; widx < this.width; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let tmpv = this.getel(hidx, widx, cidx);
                    newarr.setel(hidx, (widx + dx + this.width) % this.width, cidx, tmpv)
                }
            }
        }
        return newarr;
    }

    /**
     * rotate,旋转
     * @param {number} [degree=90] - 旋转角度,默认为90
     * @returns {ImgArray} 旋转后的结果
     */
    rotate(degree = 90) {
        degree = degree % 360;
        let rad = degree * Math.PI / 180;
        let newarr = this.empty(false);
        let cx = (this.width - 1) / 2;
        let cy = (this.height - 1) / 2;
        let cos = Math.cos(rad);
        let sin = Math.sin(rad);
        let dx = cx - cx * cos + cy * sin;
        let dy = cy - cx * sin - cy * cos;
        for (let hidx = 0; hidx < this.height; hidx++) {
            for (let widx = 0; widx < this.width; widx++) {
                let oldwidx = cos * widx - sin * hidx + dx;
                let oldhidx = sin * widx + cos * hidx + dy;
                if (!this.checkisvalid(oldhidx, oldwidx, 0)) continue;
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let tmpv = this.getel(oldhidx, oldwidx, cidx);
                    newarr.setel(hidx, widx, cidx, tmpv);
                }
            }
        }
        return newarr
    }

    //数组点运算

    /**
     * vectorize,逐元素运算,function的形式参考array.map接受的回调函数的形式
     * @param {function} func - 函数,形式为(x)=>y
     * @returns {ImgArray} 运算结果
     */
    vectorize(func) {
        let newarr = this.empty(true);
        newarr.data = this.data.map(func);
        return newarr;
    }

    //数组间的运算

    /**
     * operateArrayOperation,数组间运算,运算方式由参数func决定
     * @param {ImgArray} otherArray - 与当前数组尺寸相同
     * @param {function} func - 是一个具有两个参数的函数,返回一个值
     * @returns {ImgArray|null} 运算结果,若两数组的尺寸不匹配或类型不匹配,则返回null
     */
    operateArrayOperation(otherArray, func) {
        if (this.issameshape(otherArray)) {
            let newarr = this.empty(true);
            newarr.data = this.data.map((val, idx) => func(val, otherArray.data[idx]));
            return newarr;
        } else {
            console.error('两数组的尺寸不匹配或类型不匹配。');
            return null;
        }
    }

    /**
     * add,数组加法,支持常数加法
     * @param {ImgArray|number} otherArrayOrValue - 与当前数组尺寸相同,或者为一个常数
     * @returns {ImgArray|null} 加法后的结果,若输入的是无效的操作数,则返回null
     */
    add(otherArrayOrValue) {
        const addFunc = (x, y) => x + y;
        if (typeof otherArrayOrValue === 'number') {
            return this.vectorize(x => addFunc(x, otherArrayOrValue));
        } else if (otherArrayOrValue instanceof ImgArray) {
            return this.operateArrayOperation(otherArrayOrValue, addFunc);
        } else {
            console.error('无效的操作数。');
            return null;
        }
    }

    /**
     * sub,数组减法,支持常数或数组
     * @param {ImgArray|number} otherArrayOrValue - 与当前数组尺寸相同,或者为一个常数
     * @returns {ImgArray|null} 减法后的结果,若输入的是无效的操作数,则返回null
     */
    sub(otherArrayOrValue) {
        const subFunc = (x, y) => x - y;
        if (typeof otherArrayOrValue === 'number') {
            return this.vectorize(x => subFunc(x, otherArrayOrValue));
        } else if (otherArrayOrValue instanceof ImgArray) {
            return this.operateArrayOperation(otherArrayOrValue, subFunc);
        } else {
            console.error('无效的操作数。');
            return null;
        }
    }

    /**
     * mul,数组乘法,支持常数或数组
     * @param {ImgArray|number} otherArrayOrValue - 与当前数组尺寸相同,或者为一个常数
     * @returns {ImgArray|null} 乘法后的结果,若输入的是无效的操作数,则返回null
     */
    mul(otherArrayOrValue) {
        const mulFunc = (x, y) => x * y;
        if (typeof otherArrayOrValue === 'number') {
            return this.vectorize(x => mulFunc(x, otherArrayOrValue));
        } else if (otherArrayOrValue instanceof ImgArray) {
            return this.operateArrayOperation(otherArrayOrValue, mulFunc);
        } else {
            console.error('无效的操作数。');
            return null;
        }
    }

    /**
     * div,数组除法,支持常数或数组
     * @param {ImgArray|number} otherArrayOrValue - 与当前数组尺寸相同,或者为一个常数
     * @returns {ImgArray|null} 除法后的结果,若输入的除数数组中包含0元素或除数不合法,则返回null
     */
    div(otherArrayOrValue) {
        const divFunc = (x, y) => x / y;
        if (typeof otherArrayOrValue === 'number' && otherArrayOrValue !== 0) {
            return this.vectorize(x => divFunc(x, otherArrayOrValue));
        } else if (otherArrayOrValue instanceof ImgArray) {
            if (otherArrayOrValue.data.some(value => value === 0)) {
                console.error('除数数组中包含0元素。');
                return null;
            }
            return this.operateArrayOperation(otherArrayOrValue, divFunc);
        } else {
            console.error('除数不合法。');
            return null;
        }
    }

    /**
     * clamp,截断,将小于vmin的元素变成vmin,大于vmax的元素变成vmax
     * @param {number} [vmin=0] - 最小值,默认为0
     * @param {number} [vmax=255] - 最大值,默认为255
     * @returns {ImgArray} 截断后的结果
     */
    clamp(vmin = 0, vmax = 255) {
        return this.vectorize(x => x < vmin ? vmin : (x > vmax ? vmax : x));
    }

    /**
     * abs,对数组中的每个元素计算绝对值。返回一个与原数组尺寸相同的新数组,每个元素为原数组元素的绝对值。
     * @returns {ImgArray} 
     */
    abs() {
        return this.vectorize(x => Math.abs(x));
    }

    /**
     * square,计算数组中每个元素的平方。
     * @returns {ImgArray} 平方结果
     */
    square() {
        return this.vectorize(x => x * x);
    }

    /**
     * pow,幂运算,计算数组中每个元素的幂。
     * @param {number} value - 幂数
     * @returns {ImgArray} 幂运算结果
     */
    pow(value) {
        return this.vectorize(x => Math.pow(x, value));
    }

    /**
     * ReLU函数,将小于value的元素变成value,大于value的不变
     * @param {number} [value=0] - 阈值,默认为0
     * @returns {ImgArray} ReLU函数结果
     */
    relu(value = 0) {
        return this.vectorize(x => x < value ? value : x);
    }

    /**
     * opposite,取反,即每个元素取负
     * @returns {ImgArray} 取反结果
     */
    opposite() {
        return this.vectorize(x => -x);
    }

    /**
     * vectorizemath,对Math中的函数进行运算
     * @returns {ImgArray} 函数运算结果
     */
    vectorizemath(func = Math.sin, ...params) {
        //可以看成是一个母函数,只要是调用了Math的函数,都可以用这个函数,
        //例如:前面的pow()方法,写成vectorizemath(Math.pow,y)
        //例如:前面的abs()方法,写成vectorizemath(Math.abs)
        return this.vectorize(x => func(x, ...params))
    }


    /**
     * todo:分成5个函数,分别对应不同的阈值处理方式
     * @param {number} [threshold=100] - 阈值,默认为100
     * @param {string} [method='binary'] - 阈值处理方式,默认为'binary',可选值有'binary'、'truncate'、'binary_inv'、'tozero'、'tozero_inv'
     * @param {number} [maxval=255] - 最大值,默认为255
     * @returns {ImgArray} 阈值处理结果
     */
    threshold(threshold = 100, method = 'binary', maxval = 255) {
        if (method == 'binary') {
            //二值化
            return this.vectorize((x) => { return x > threshold ? maxval : 0 })
        }
        else if (method == 'truncate') {
            //截断化
            return this.vectorize((x) => { return x > threshold ? threshold : x })
        }
        else if (method == 'binary_inv') {
            //二值化(反转)
            return this.vectorize((x) => { return x > threshold ? 0 : maxval })
        }
        else if (method == 'tozero') {
            //归零化
            return this.vectorize((x) => { return x > threshold ? x : 0 })
        }
        else if (method == 'tozero_inv') {
            //归零化(反转) 
            return this.vectorize((x) => { return x > threshold ? 0 : x })
        }
    }

    /**
     * span 区间变换
     * @param {number} lower - 下限
     * @param {number} upper - 上限
     * @param {number} [vmin=0] - 最小值,默认为0
     * @param {number} [vmax=255] - 最大值,默认为255
     * @returns {ImgArray} 区间变换结果
     */
    span(lower, upper, vmin = 0, vmax = 255) {
        //区间变换
        let func = (x) => {
            if (x >= upper) return vmax;
            if (x <= lower) return vmin;
            return (x - lower) / (upper - lower) * (vmax - vmin) + vmin;
        }
        return this.vectorize(func);
    }

    /**
     * globalminmax,获取整个数组的最小值和最大值
     * @returns {Object} 包含最小值和最大值的对象
     */
    globalminmax() {
        //获取整个数组的最小值和最大值
        let minv = this.data[0];
        let maxv = this.data[0];
        this.data.forEach((x) => {
            if (x < minv) {
                minv = x;
                return
            }
            if (x > maxv) {
                maxv = x;
                return
            }
        });
        return { minv, maxv };
    }

    /**
     * stretch,拉伸到指定的数值范围内
     * @param {number} [vmin=0] - 最小值,默认为0
     * @param {number} [vmax=255] - 最大值,默认为255
     * @returns {ImgArray} 拉伸后的结果
     */
    stretch(vmin = 0, vmax = 255) {
        //拉伸到指定的数值范围内
        let { minv, maxv } = this.globalminmax();
        if (minv == maxv) {
            return this.copy().fill((vmin + vmax) / 2);
        }
        else {
            return this.vectorize((x) => { return (x - minv) / (maxv - minv) * (vmax - vmin) + vmin });
        }
    }

    /**
     * pad,对高和宽进行常数填充的扩边操作,顺序是左上右下
     * @param {Object} options - 填充选项
     * @param {number} [options.l=1] - 左边填充的个数,默认为1
     * @param {number} [options.r=2] - 右边填充的个数,默认为2
     * @param {number} [options.t=3] - 上边填充的个数,默认为3
     * @param {number} [options.b=4] - 下边填充的个数,默认为4
     * @param {number} [options.fillvalue=0] - 填充的值,默认为0
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含填充后的数据。
     */
    pad({ l = 1, r = 2, t = 3, b = 4, fillvalue = 0 } = {}) {
        let newheight = this.height + t + b;
        let newwidth = this.width + l + r;
        let newarr = new ImgArray({ height: newheight, width: newwidth, channel: this.channel }).fill(fillvalue);
        for (let hidx = t; hidx < this.height + t; hidx++) {
            for (let widx = l; widx < this.width + l; widx++) {
                for (let cidx = 0; cidx < this.channel; cidx++) {
                    let ohidx = hidx - t;
                    let owidx = widx - l;
                    let tmpv = this.getel(ohidx, owidx, cidx);
                    newarr.setel(hidx, widx, cidx, tmpv);
                }
            }
        }
        return newarr;
    }

    /**
     * slice,对高和宽进行切片操作
     * @param {number} sthidx - 高的起始位置(不包含结束位置)
     * @param {number} edhidx - 高的结束位置(不包含结束位置)
     * @param {number} stwidx - 宽的起始位置(不包含结束位置)
     * @param {number} edwidx - 宽的结束位置(不包含结束位置)
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含切片后的数据。
     */
    slice(sthidx, edhidx, stwidx, edwidx) {
        //参数含义分别别是高的起始和结束位置(不包含结束位置),宽的起始和结束位置(不包含结束位置)
        //返回一个新的ImgArray对象
        if (this.checkisvalid(sthidx, stwidx, 0) && this.checkisvalid(edhidx - 1, edwidx - 1, 0)) {
            let newheight = edhidx - sthidx;
            let newwidth = edwidx - stwidx;
            let newimgarr = new ImgArray({ height: newheight, width: newwidth, channel: this.channel });
            for (let hidx = 0; hidx < newheight; hidx++) {
                for (let widx = 0; widx < newwidth; widx++) {
                    for (let cidx = 0; cidx < this.channel; cidx++) {
                        let tmpv = this.getel(hidx + sthidx, widx + stwidx, cidx);
                        newimgarr.setel(hidx, widx, cidx, tmpv);
                    }
                }
            }
            return newimgarr;
        }
        console.error('数组索引越界。')
    }

    /**
     * structure,按照结构元素进行邻域展开
     * @param {Array} pattern - 结构元素,是一个二维数组,表示邻域的形状
     * @param {number} [fillvalue=0] - 填充的值,默认为0
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含邻域展开后的数据。
     */
    structure(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        let kernelheight = pattern.length;
        let kernelwidth = pattern[0].length;

        let pady = Math.floor(kernelheight / 2);
        let padx = Math.floor(kernelwidth / 2);

        let padimgar = this.pad({ l: padx, t: pady, r: padx, b: pady, fillvalue });
        let imgarrs = [];
        for (let idxh = 0; idxh < kernelheight; idxh++) {
            for (let idxw = 0; idxw < kernelwidth; idxw++) {
                if (pattern[idxh][idxw] == 0) continue;
                imgarrs.push(padimgar.slice(idxh, idxh + this.height, idxw, idxw + this.width));
            }
        }
        let [baseimg, ...resimgs] = imgarrs;
        return baseimg.dstack(...resimgs);
    }

    /**
     * neighbor,按尺寸进行邻域展开,sizes是2个元素组成的数组,表示邻域的高和宽,里边应当是奇数
     * 该方法实际上不操作,只生成pattern,调用strcuture完成邻域的生成
     * @param {number[]} [sizes = [3, 3]] - 邻域的尺寸,是一个2个元素的数组,表示邻域的高和宽
     * @param {number} [fillvalue=0] - 填充的值,默认为0
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含邻域展开后的数据。
     */
    neighbor(sizes = [3, 3], fillvalue = 0) {
        let pt = [];
        for (let idxh = 0; idxh < sizes[0]; idxh++) {
            pt.push(Array(sizes[1]).fill(1));
        }
        return this.structure(pt, fillvalue);
    }

    /**
     * apply_along_channel,沿通道的运算
     * @param {function} func - 一回调函数接收一个一维数组,返回一个数值
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含运算后的数据。
     */
    apply_along_channel(func) {
        let outimgarr = new ImgArray({ height: this.height, width: this.width, channel: 1 });
        for (let idxh = 0; idxh < this.height; idxh++) {
            for (let idxw = 0; idxw < this.width; idxw++) {
                let stidx = this.idxtoelidx(idxh, idxw, 0);
                let data = this.data.slice(stidx, stidx + this.channel);
                let value = func(data);
                outimgarr.setel(idxh, idxw, 0, value);
            }
        }
        return outimgarr;
    }

    /**
     * mean,计算均值
     * @param {boolean} [ischannel=false] - 当ischannel为True时,沿通道计算均值,否则计算全局均值
     * @returns {number|ImgArray} 返回一个数值或一个新的 `ImgArray` 实例,包含均值或均值数组。
     */
    mean(ischannel = false) {
        if (ischannel) {
            let func = (x) => { return x.reduce((prev, curr) => { return prev + curr }) / x.length };
            return this.apply_along_channel(func);
        }
        return this.data.reduce((prev, curr) => { return prev + curr }) / this.data.length;
    }

    /**
     * max,计算最大值,膨胀,最大值池化
     * @param {boolean} [ischannel=false] - 当ischannel为True时,沿通道计算最大值,否则计算全局最大值
     * @returns {number|ImgArray} 返回一个数值或一个新的 `ImgArray` 实例,包含最大值或最大值数组。
     */
    max(ischannel = false) {
        if (ischannel) {
            let func = (x) => { return Math.max(...x) }
            return this.apply_along_channel(func);
        }
        let maxv = this.data[0];
        this.data.forEach((x) => { if (x > maxv) maxv = x });
        return maxv;
    }

    /**
     * min,计算最小值,腐蚀,最小值池化
     * @param {boolean} [ischannel=false] - 当ischannel为True时,沿通道计算最小值,否则计算全局最小值
     * @returns {number|ImgArray} 返回一个数值或一个新的 `ImgArray` 实例,包含最小值或最小值数组。
     */
    min(ischannel = false) {
        if (ischannel) {
            let func = (x) => { return Math.min(...x) }
            return this.apply_along_channel(func)
        }
        let minv = this.data[0];
        this.data.forEach((x) => { if (x < minv) minv = x });
        return minv;
    }

    /**
     * linearc,线性加权
     * @param {number[]} [weights = [1, 2, 3]] - 线性加权系数,是一个一维数组,表示每个通道的权重
     * @param {number} [bais=0] - 偏差值,默认为0
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含线性加权后的数据。
     */
    linearc(weights = [1, 2, 3], bais = 0) {
        //判断weights的长度与通道数相同
        let func = (x) => {
            let res = 0
            for (let i = 0; i < weights.length; i++) {
                res += x[i] * weights[i]
            }
            return res + bais
        }
        return this.apply_along_channel(func)
    }

    /**
     * conv2d,卷积运算,weights卷积
     * @param {number[][]} [weights = [[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]]] - 卷积核,是一个二维数组,表示卷积核的权重
     * @param {number} [bias=0] - 偏差值,默认为0
     * @param {number} [fillvalue=0] - 填充的值,默认为0
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含卷积运算后的数据。
     */
    conv2d(weights = [[1 / 9, 1 / 9, 1 / 9], [1 / 9, 1 / 9, 1 / 9], [1 / 9, 1 / 9, 1 / 9]], bias = 0, fillvalue = 0) {
        //卷积运算,只对通道数为1的数组有效
        let size = [weights.length, weights[0].length]
        let nb = this.neighbor(size, fillvalue)
        return nb.linearc(weights.flat(), bias)
    }

    /**
     * median,中值滤波,对通道数为1的数组结果正确
     * @param {number[][]} [sizes = [3, 3]] - 邻域大小,是一个二维数组,表示邻域的大小
     * @param {number} [fillvalue=0] - 填充的值,默认为0
     * @returns {ImgArray} 返回一个新的 `ImgArray` 实例,包含中值滤波后的数据。
     */
    median(sizes = [3, 3], fillvalue = 0) {
        let tmparr = this.neighbor(sizes, fillvalue)
        /**
         * 计算数组的中值。
         * 对传入的数组进行排序并计算中值。如果数组长度是偶数,则返回中间两个元素的平均值。
         * @param {number[]} arr - 需要计算中值的数组。
         * @returns {number} 返回中值。
         */
        function calcmedian(arr) {
            arr.sort((a, b) => a - b);
            const mid = Math.floor(arr.length / 2);
            if (arr.length % 2 === 0) {
                return (arr[mid - 1] + arr[mid]) / 2;
            } else {
                return arr[mid];
            }
        }
        return tmparr.apply_along_channel(calcmedian);
    }

    /**
     * gaussianBlur,应用高斯模糊。
     * 该方法生成一个高斯核,并将其应用于图像数据,实现高斯模糊效果。
     * @param {number} [sigma=2] - 高斯核的标准差,控制模糊的程度。默认为 `2`。
     * @param {number[]} [kernel_size=[3, 3]] - 高斯核的尺寸,默认为 `[3, 3]`。尺寸应该为奇数。
     * @param {number} [fillvalue=0] - 邻域填充的默认值,默认为 `0`。
     * @returns {ImgArray} 返回一个新的图像数据对象,应用了高斯模糊。
     */
    gaussianBlur(sigma = 2, kernel_size = [3, 3], fillvalue = 0) {
        let tmparr = this.neighbor(kernel_size, fillvalue)
        /**
         * 创建高斯核。
         * 根据给定的标准差和核大小生成一个高斯核,并进行归一化处理。
         * @param {number} sigma - 高斯核的标准差。
         * @param {number[]} kernel_size - 高斯核的尺寸。
         * @returns {number[][]} 返回一个归一化的高斯核。
         */
        function creategausskernel(sigma, kernel_size) {
            let kernel = [];
            let center = Math.floor(kernel_size[0] / 2);
            for (let i = 0; i < kernel_size[0]; i++) {
                let row = [];
                for (let j = 0; j < kernel_size[1]; j++) {
                    let x = i - center;
                    let y = j - center;
                    let value = (1 / (2 * Math.PI * sigma * sigma)) * Math.exp(-(x * x + y * y) / (2 * sigma * sigma));
                    row.push(value);
                }
                kernel.push(row);
            }
            // 归一化
            let sum = kernel.reduce((a, b) => a.concat(b)).reduce((a, b) => a + b);
            for (let i = 0; i < kernel_size[0]; i++) {
                for (let j = 0; j < kernel_size[1]; j++) {
                    kernel[i][j] /= sum;
                }
            }
            return kernel;
        }
        return tmparr.conv2d(creategausskernel(sigma, kernel_size));
    }

    /**
     * sobelx,对图像应用 Sobel 算子,计算图像在 X 方向上的梯度。
     * @returns {ImgArray} 返回计算后的图像数据对象。
     */
    sobelx() {
        let kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
        return this.conv2d(kernel);
    }

    /**
      * sobely,对图像应用 Sobel 算子,计算图像在 Y 方向上的梯度。
      * @returns {ImgArray} 返回计算后的图像数据对象。
      */
    sobely() {
        let kernel = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
        return this.conv2d(kernel)
    }

    /**
     * sobelxy,计算图像的梯度幅值,结合 Sobel X 和 Sobel Y 方向的梯度。
     * @returns {ImgArray} 返回图像的梯度幅值。
     */
    sobelxy() {
        return this.sobelx().abs().add(this.sobely().abs());
    }

    /**
     * laplacian,应用拉普拉斯算子对图像进行边缘检测。
     * @param {number} [size=4] - 拉普拉斯核的大小,默认为 `4`。
     * @returns {ImgArray} 返回一个新的图像数据对象,应用了拉普拉斯算子。
     */
    laplacian(size = 4) {
        let kernel4 = [[0, -1, 0], [-1, 4, -1], [0, -1, 0]];
        let kernel8 = [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]];
        let kernel = size == 4 ? kernel4 : kernel8;
        return this.conv2d(kernel)
    }

    /**
     * sharrx,对图像应用 Sobel 算子,计算图像在 X 方向上的梯度。
     * @returns {ImgArray} 返回计算后的图像数据对象。
     */
    sharrx() {
        let kernel = [[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]];
        return this.conv2d(kernel);
    }

    /**
     * sharry,对图像应用 Sobel 算子,计算图像在 Y 方向上的梯度。
     * @returns {ImgArray} 返回计算后的图像数据对象。
     */
    sharry() {
        let kernel = [[-3, -10, -3], [0, 0, 0], [3, 10, 3]];
        return this.conv2d(kernel);
    }

    /**
     * sharrxy,计算图像的梯度幅值,通过结合 Sobel X 和 Sobel Y 方向的梯度计算结果。
     * @returns {ImgArray} 返回图像的梯度幅值。
     */
    sharrxy() {
        return this.sharrx().abs().add(this.sharry().abs());
    }

    /**
     * prewittx,对图像应用 Prewitt 算子,计算图像在 X 方向上的梯度。
     * @returns {ImgArray} 返回计算后的图像数据对象。
     */
    prewittx() {
        let kernel = [[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]];
        return this.conv2d(kernel);
    }
    /**
     * prewitty,对图像应用 Prewitt 算子,计算图像在 Y 方向上的梯度。
     * @returns {ImgArray} 返回计算后的图像数据对象。
     */
    prewitty() {
        let kernel = [[-1, -1, -1], [0, 0, 0], [1, 1, 1]];
        return this.conv2d(kernel)
    }
    /**
     * prewittxy,计算图像的梯度幅值,结合 Prewitt X 和 Prewitt Y 方向的梯度。
     * @returns {ImgArray} 返回图像的梯度幅值。
     */
    prewittxy() {
        return this.prewittx().abs().add(this.prewitty().abs());
    }

    //canny算子,有问题
    /**
     * canny,应用 Canny 算子。对图像进行边缘检测
     * @returns {ImgArray} 返回一个新的图像数据对象,应用了 Canny 算子。
     */
    canny() {
        let tmparr = this.structure([[1, 1, 1], [1, -7, 1], [1, 1, 1]], 0)
        return tmparr.conv2d(creategausskernel(1, 3))
    }

    /**
     * maxpooling,应用最大值池化对图像进行降采样。
     * @param {number[]} [size=[3, 3]] - 池化核的大小,默认为 `[3, 3]`。
     * @param {number} [fillvalue=0] - 填充值,默认为 `0`。
     * @returns {ImgArray} 返回一个新的图像数据对象,应用了最大值池化。
     */
    maxpooling(size = [3, 3], fillvalue = 0) {
        //最大值池化
        let tmparr = this.neighbor(size, fillvalue);
        return tmparr.max(true);
    }

    /**
     * minpooling,应用最小值池化对图像进行降采样。
     * @param {number[]} [size=[3, 3]] - 池化核的大小,默认为 `[3, 3]`。
     * @param {number} [fillvalue=0] - 填充值,默认为 `0`。
     * @returns {ImgArray} 返回一个新的图像数据对象,应用了最小值池化。
     */
    minpooling(size = [3, 3], fillvalue = 0) {
        //最小值池化
        let tmparr = this.neighbor(size, fillvalue);
        return tmparr.min(true);
    }

    /**
     * avgpooling,应用平均值池化对图像进行降采样。
     * @param {number[]} [size=[3, 3]] - 池化核的大小,默认为 `[3, 3]`。
     * @param {number} [fillvalue=0] - 填充值,默认为 `0`。
     * @returns {ImgArray} 返回一个新的图像数据对象,应用了平均值池化。
     */
    avgpooling(size = [3, 3], fillvalue = 0) {
        //平均池化(均值滤波)
        let tmparr = this.neighbor(size, fillvalue);
        return tmparr.mean(true);
    }

    /**
     * dilate,膨胀操作,使用给定的结构元素对图像进行膨胀。
     * @param {number[][]} [pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]]] - 膨胀操作的结构元素,通常是一个矩阵,表示膨胀的形状和大小。默认为 3x3 的邻域。
     * @param {number} [filfillvalue = 0] - 膨胀操作中填充区域的值,默认为 0。
     * @returns {ImgArray} 返回膨胀后的图像数据对象。
     */
    dilate(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        let tmparr = this.structure(pattern, fillvalue);
        return tmparr.max(true);
    }

    /**
     * erode,腐蚀操作,使用给定的结构元素对图像进行腐蚀。
     * @param {number[][]} [pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]]] - 腐蚀操作的结构元素,通常是一个矩阵,表示腐蚀的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 腐蚀操作中填充区域的值,默认为 0。
     * @returns {ImgArray} 返回腐蚀后的图像数据对象。
     */
    erode(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        let tmparr = this.structure(pattern, fillvalue);
        return tmparr.min(true);
    }

    /**
     * mgradient,形态学梯度操作,使用给定的结构元素对图像进行形态学梯度操作。
     * @param {number[][]} [sizes = [3, 3]] - 形态学梯度操作的结构元素,通常是一个矩阵,表示形态学梯度的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 形态学梯度操作中填充区域的值,默认为 0。
     * @returns {ImgArray} 返回形态学梯度后的图像数据对象。
     */
    mgradient(sizes = [3, 3], fillvalue = 0) {
        let tmparr = this.neighbor(sizes, fillvalue);
        return tmparr.max(true).sub(tmparr.min(true));
    }

    /**
     * open,开运算操作,使用给定的结构元素对图像进行开运算。
     * @param {number[][]} [pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]]] - 开运算的结构元素,通常是一个矩阵,表示开运算的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 开运算中填充区域的值,默认为 0。
     * @returns {ImgArray} 返回开运算后的图像数据对象。
     */
    open(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        return this.erode(pattern, fillvalue).dilate(pattern, fillvalue);
    }

    /**
     * close,闭运算操作,使用给定的结构元素对图像进行闭运算。
     * @param {number[][]} [pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]]] - 闭运算的结构元素,通常是一个矩阵,表示闭运算的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 闭运算中填充区域的值,默认为 0。
     * @returns {ImgArray} 返回闭运算后的图像数据对象。
     */
    close(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        return this.dilate(pattern, fillvalue).erode(pattern, fillvalue);
    }

    /**
     * tophat,顶帽运算,使用给定的结构元素对图像进行顶帽运算。
     * @param {number[][]} [pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]]] - 顶帽运算的结构元素,通常是一个矩阵,表示顶帽运算的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 顶帽运算中填充区域的值,默认为 0。
     * @returns {ImgArray} 返回顶帽运算后的图像数据对象。
     */
    tophat(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        return this.sub(this.open(pattern, fillvalue));
    }

    /**
     * blackhat,黑帽运算,使用给定的结构元素对图像进行黑帽运算。
     * @param {number[][]} [pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]]] - 黑帽运算的结构元素,通常是一个矩阵,表示黑帽运算的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 黑帽运算中填充区域的值,默认为
     * @returns {ImgArray} 返回黑帽运算后的图像数据对象。
     */
    blackhat(pattern = [[1, 1, 1], [0, 1, 0], [1, 1, 1]], fillvalue = 0) {
        return this.close(pattern, fillvalue).sub(this);
    }

    /**
     * adaptiveThreshold,自适应阈值分割,使用给定的结构元素对图像进行自适应阈值分割。
     * @param {number[][]} [sizes = [3, 3]] - 自适应阈值分割的结构元素,通常是一个矩阵,表示自适应阈值分割的形状和大小。默认为 3x3 的邻域。
     * @param {number} [fillvalue = 0] - 自适应阈值分割中填充区域的值,默认为 0。
     * @param {number} [constant = 0] - 自适应阈值分割的常数,默认为 0。
     * @returns {ImgArray} 返回自适应阈值分割后的图像数据对象。
     */
    adaptiveThreshold(sizes = [3, 3], fillvalue = 0, constant = 0) {
        //自适应阈值分割,后续讨论,有问题
        let tmparr = this.neighbor(sizes, fillvalue);
        let res = new Tensor(this.width, this.height);
        for (let i = 0; i < this.width; i++) {
            for (let j = 0; j < this.height; j++) {
                let sum = 0;
                for (let m = 0; m < sizes[0]; m++) {
                    for (let n = 0; n < sizes[1]; n++) {
                        sum += tmparr.get(m, n);
                    }
                }
                let mean = sum / (sizes[0] * sizes[1]);
                res.set(i, j, this.get(i, j) > mean - constant ? 1 : 0);
            }
        }
        return res;
    }

    /**
     * lbp,计算经典的局部二值模式(LBP)特征。
     * LBP 用于描述图像的局部纹理特征,常用于图像分类和面部识别等任务。
     * @returns {ImgArray} 返回包含 LBP 特征的图像数据对象。
     */
    lbp() {
        let tmparr = this.neighbor([3, 3], 0);
        /**
         * 计算 LBP 特征的函数
         * @param {number[]} x - 包含 9 个元素的数组,表示 3x3 邻域的像素值。
         * @returns {number} 返回计算后的 LBP 特征值(十进制)。
         */
        function calclbp(x) {
            let tmpnew = 0;
            let weights = [128, 64, 32, 1, 0, 16, 2, 4, 8];
            for (let i = 0; i < 9; i++) {
                if (x[i] > x[4]) {
                    tmpnew += weights[i];
                }
            }
            return tmpnew;
        }
        return tmparr.apply_along_channel(calclbp);
    }

    /**
     * lmi,计算 LMI 特征。
     * LMI 用于描述图像的局部纹理特征,常用于图像分类和面部识别等任务。
     * @param {number[][]} [sizes = [3, 3]] - LMI 特征的邻域大小,默认为 3x3 的邻域。
     * @returns {ImgArray} 返回包含 LMI 特征的图像数据对象。
     */
    lmi(sizes = [3, 3]) {
        let tmparr = this.neighbor(sizes, 0);
        let centerpos = Math.floor(tmparr.shape.channel / 2);
        /**
         * 计算 LMI 特征的函数
         * @param {number[]} x - 表示邻域的像素值。
         * @returns {number} 返回计算后的 LMI 特征值(十进制)。
         */
        function calclmi(x) {
            let tmpnew = 0;
            let centvalue = x[centerpos];
            for (let i = 0; i < x.length; i++) {
                if (x[i] < centvalue) {
                    tmpnew += 1;
                }
            }
            return tmpnew;
        }
        return tmparr.apply_along_channel(calclmi);
    }

    /**
     * cellsim,计算元胞自动机(Cellular Automaton)特征。
     * 元胞自动机是一种描述自然界中复杂规则的数学模型,常用于图像处理和计算机视觉等应用。
     * @param {number[][]} [sizes = [3, 3]] - 元胞自动机特征的邻域大小,默认为 3x3 的邻域。
     * @returns {ImgArray} 返回包含元胞自动机特征的图像数据对象。
     */
    cellsim(sizes = [3, 3]) {
        //元胞自动机,生命游戏
        // 生命游戏规则:生命游戏是一种著名的二维元胞自动机,其规则非常简单,每个元胞根据其周围八个相邻元胞的状态来改变自己的状态,规则定义如下:
        // 如果一个元胞的状态为死亡(0),且周围有三个元胞的状态为存活(1),则该元胞下一个状态为存活(1);
        // 如果一个元胞的状态为存活(1),且周围有两个或三个元胞的状态也为存活(1),则该元胞下一个状态仍为存活(1);
        // 其他情况下,该元胞下一个状态为死亡(0)。
        let tmparr = this.neighbor(sizes, 0);
        function calcell(x) {
            let tmpnew = 0;
            let centerpos = Math.floor(x.length / 2);
            for (let i = 0; i < x.length; i++) {
                if (x[i] >= 0.5) {
                    tmpnew += 1;
                }
            }
            if (x[centerpos] >= 0.5) {
                if (tmpnew == 4 || tmpnew == 3)
                    return 1;
                else return 0;

            } else {
                if (tmpnew == 3)
                    return 1;
                else return 0;
            }
        }
        return tmparr.apply_along_channel(calcell);
    }

    /**
     * copy,复制当前图像数据对象,创建一个新副本。
     * @returns {ImgArray} 当前图像数据的副本。
     */
    copy() {
        let newarr = this.empty(true);
        newarr.data = this.data.slice();
        return newarr;
    }

    /**
     * empty,创建一个与当前图像数据对象相同尺寸的空数组,考虑了ImgArray和Img实例。
     * @param {boolean} [lazy = false] - 是否启用懒加载,默认为 false。
     * @returns {ImgArray} 创建的空数组。
     */
    empty(lazy = false) {
        if (this.constructor.name == "ImgArray") {
            return new ImgArray({
                height: this.height,
                width: this.width,
                channel: this.channel,
                lazy,
                dtype: this.dtype
            })
        }
        else {
            return new Img({
                height: this.height,
                width: this.width,
                lazy
            })
        }
    }

    /**
     * meshgrid,创建一个规则格网,用于在图像处理中生成网格特征。
     * @static
     * @param {number[]} [hrange = [0, 256]] - 格网在垂直方向的范围,默认为 [0, 256]。
     * @param {number[]} [wrange = [0, 256]] - 格网在水平方向的范围,默认为 [0, 256]。
     * @returns {ImgArray[]} 返回一个包含两个 ImgArray 对象的数组,其中第一个数组表示垂直方向的格网,第二个数组表示水平方向的格网。
     */
    static meshgrid(hrange = [0, 256], wrange = [0, 256]) {
        //产生规则格网
        let width = wrange[1] - wrange[0]
        let height = hrange[1] - hrange[0]
        let ximgarr = new ImgArray({ height, width, channel: 1 })
        let yimgarr = new ImgArray({ height, width, channel: 1 })
        for (let hidx = 0; hidx < height; hidx++) {
            for (let widx = 0; widx < width; widx++) {
                ximgarr.setel(hidx, widx, 0, widx + wrange[0]);
                yimgarr.setel(hidx, widx, 0, hidx + hrange[0]);
            }
        }
        return [yimgarr, ximgarr];
    }

    /**
     * random,创建一个指定尺寸的,指定范围的均值分布的数组。
     * @static
     * @param {Object} options - 创建数组的选项。
     * @param {number} [options.height=256] - 数组的高度,默认为 256。
     * @param {number} [options.width=256] - 数组的宽度,默认为 256。
     * @param {number} [options.channel=3] - 数组的通道数,默认为 3。
     * @param {number} [options.vmin=0] - 数组元素的最小值,默认为 0。
     * @param {number} [options.vmax=255] - 数组元素的最大值,默认为 255。
     * @returns {ImgArray} 返回一个 ImgArray 对象,表示创建的随机数组。
     */
    static random({ height = 256, width = 256, channel = 3, vmin = 0, vmax = 255 } = {}) {
        let newimgarr = new ImgArray({ height, width, channel, lazy: false });
        newimgarr.data = newimgarr.data.map(x => Math.random() * (vmax - vmin) + vmin);
        return newimgarr;
    }

    /**
     * zeros,创建一个指定尺寸的零数组。
     * @static
     * @param {Object} options - 创建数组的选项。
     * @param {number} [options.height=256] - 数组的高度,默认为 256。
     * @param {number} [options.width=256] - 数组的宽度,默认为 256。
     * @param {number} [options.channel=3] - 数组的通道数,默认为 3。
     * @returns {ImgArray} 返回一个 ImgArray 对象,表示创建的零数组。
     */
    static zeros({ height = 256, width = 256, channel = 3 } = {}) {
        return new ImgArray({ height, width, channel, lazy: false });
    }

    /**
     * ones,创建一个指定尺寸的 ones 数组。
     * @static
     * @param {Object} options - 创建数组的选项。
     * @param {number} [options.height=256] - 数组的高度,默认为 256。
     * @param {number} [options.width=256] - 数组的宽度,默认为 256。
     * @param {number} [options.channel=3] - 数组的通道数,默认为 3。
     * @returns {ImgArray} 返回一个 ImgArray 对象,表示创建的 ones 数组。
     */
    static ones({ height = 256, width = 256, channel = 3 } = {}) {
        return new ImgArray({ height, width, channel, lazy: false }).fill(1);
    }

    /**
     * full,创建一个指定尺寸的指定值数组。
     * @static
     * @param {Object} options - 创建数组的选项。
     * @param {number} [options.height=256] - 数组的高度,默认为 256。
     * @param {number} [options.width=256] - 数组的宽度,默认为 256。
     * @param {number} [options.channel=3] - 数组的通道数,默认为 3。
     * @param {number} [options.value=0] - 数组元素的值,默认为 0。
     * @returns {ImgArray} 返回一个 ImgArray 对象,表示创建的指定值数组。
     */
    static full({ height = 256, width = 256, channel = 3 } = {}, value = 0) {
        return new ImgArray({ height, width, channel, lazy: false }).fill(value);
    }

    /**
     * fromBuffer,从一个 ArrayBuffer 创建一个 ImgArray。
     * @static
     * @param {ArrayBuffer} arraybuffer - 用于创建 ImgArray 的 ArrayBuffer。
     * @param {ImgArray} shape - 创建 ImgArray 的形状。
     * @param {string} [dtype='float32'] - 创建 ImgArray 的数据类型,默认为 'float32'。
     * @returns {ImgArray} 返回一个ImgArray 对象,表示从 ArrayBuffer 创建的 ImgArray
     * @throws {Error} 如果 dtype 不是 ImgArray 支持的数据类型,则抛出异常。
     * @throws {Error} 如果 ArrayBuffer 的长度与 shape 不匹配,则抛出异常。
     */
    static fromBuffer(arraybuffer, shape, dtype = 'float32') {
        // arraybuffer创建ImgArray,假设buffer的排列方向是hwc,将buffer根据dtype转换为typedarray
        // 参照random函数,检查转换后的和shape个数一致
        let { height, width, channel } = shape;

        let type = ImgArray.dtypes[dtype];
        if (type == undefined) {
            throw new Error(`dtype ${dtype} is not supported, supported dtypes are ${ImgArray.dtypenames}`);
        }
        let bytesize = height * width * channel * type.BYTES_PER_ELEMENT;
        // 确保转换后的数组长度与期望的shape一致
        if (bytesize !== arraybuffer.byteLength) {
            throw new Error('长度不匹配');
        }
        let typedArray = new type(arraybuffer.slice()); //对arraybuffer拷贝
        // 创建ImgArray实例,并将数据赋值
        let newimgarr = new ImgArray({ height, width, channel, lazy: true });
        newimgarr.data = typedArray;
        return newimgarr;
    }

    /**
     * buffer,获取 ImgArray 的 ArrayBuffer。
     * @type {ArrayBuffer}
     */
    get buffer() {
        return this.data.buffer;
    }

    /**
     * typedarray,获取 ImgArray 的 TypedArray。
     * @type {TypedArray} 
     */
    get typedarray() {
        return this.data;
    }

    /**
     * fromArray,从一个数组创建一个 ImgArray。
     * @static
     * @param {Array} arr - 用于创建 ImgArray 的数组。
     * @param {ImgArray} shape - 创建 ImgArray 的形状。
     * @param {string} [dtype='float32'] - 创建 ImgArray 的数据类型,默认为 'float32'。
     * @returns {ImgArray} 返回一个ImgArray 对象,表示从数组创建的 ImgArray
     * @throws {Error} 如果数组的类型与 ImgArray 的类型不匹配,则抛出异常。
     */
    static fromArray(arr, shape, dtype = 'float32') {
        //从数组创建ImgArray,arr的类型typedarray或array,检查个数、长度、类型是否一致,多维array的话flatten为一维
        if (!Array.isArray(arr) && !ArrayBuffer.isView(arr)) {
            throw new Error('arr输入类型必须是:array or TypedArray');
        }
        let flatArray;
        if (Array.isArray(arr)) {
            // 展开数组
            flatArray = arr.flat(Infinity);     // 当 depth 设置为 Infinity 时,会将数组的所有嵌套层级全部展开
        } else {
            // 转换为普通数组
            flatArray = arr;
        }
        let { height, width, channel } = shape;
        if (flatArray.length !== height * width * channel) {
            throw new Error('长度不匹配');
        }
        let newimgarr = new ImgArray({ height, width, channel, lazy: true });
        newimgarr.data = new ImgArray.dtypes[dtype](flatArray);
        return newimgarr;
    }

    /**
     * dist,计算数组在通道方向与一个向量的欧氏距离。
     * @param {number[]} [v2 = [3, 4, 5]] - 用于计算距离的向量。
     * @returns {ImgArray} 返回一个 ImgArray 对象,表示计算得到的距离数组。
     */
    dist(v2 = [3, 4, 5]) {
        //计算数组在通道方向与一个向量的欧氏距离
        if (v2.length != this.channel) {
            console.error('数组长度与通道数不匹配。');
            return;
        }
        let func = (v1) => {
            let res = 0
            for (let i = 0; i < v2.length; i++) {
                res += (v1[i] - v2[i]) * (v1[i] - v2[i]);
            }
            return Math.sqrt(res);
        }
        return this.apply_along_channel(func)
    }

    /**
     * cossimdist,计算数组在通道方向与一个向量的余弦距离。
     * @param {number[]} [v2 = [3, 4, 5]] - 用于计算距离的向量。
     * @returns {ImgArray} 返回一个 ImgArray 对象,表示计算得到的距离数组。
     */
    cossimdist(v2 = [3, 4, 5]) {
        //计算数组在通道方向与一个向量的余弦距离
        if (v2.length != this.channel) {
            console.error('数组长度与通道数不匹配。');
            return;
        }
        let l2 = Math.hypot(...v2)
        let func = (v1) => {
            let res = 0
            for (let i = 0; i < v2.length; i++) {
                res += v1[i] * v2[i]
            }
            return 1 - res / (l2 * Math.hypot(...v1) + 0.0000001)
        }

        return this.apply_along_channel(func)
    }

    /**
     * kmeans,使用 K 均值聚类算法对图像数据进行聚类。
     * @param {number} k - 聚类的数量。
     * @param {Array<Array<number>> | null} centers - 初始聚类中心,是一个二维数组(kxchannel)。如果为 `null`,程序将随机选择初始中心。
     * @param {string} [disttype='dist'] - 距离度量方式,可选值为 'dist'(欧氏距离)或 'cossimdist'(余弦相似度)。
     * @returns {Array} 返回两个值:
     * - `newcenters`:更新后的聚类中心。
     * - `clusterresult`:一个 ImgArray 对象,表示每个像素的聚类结果。
     */
    kmeans(k = 4, centers = null, disttype = 'dist') {
        //k均值聚类
        //k表示聚类数量,
        //centers是一个kxchannel的二维列表构成的数组,表示k个聚类中心,当为null时程序自动随机选择
        //disttype是距离度量方式可以为dist或者是cossimdist,
        //初始化聚类中心
        if (centers === null) {
            centers = []
            for (let i = 0; i < k; i++) {
                let tmpx = parseInt(Math.random() * this.width)
                let tmpy = parseInt(Math.random() * this.height)
                let idx = this.idxtoelidx(tmpy, tmpx, 0)
                let v2 = this.data.slice(idx, idx + this.channel)
                v2 = Array.from(v2)
                centers.push(v2)
            }
        }
        else {
            k = centers.length
        }
        //对聚类中心按照特征值排一下序,保证不同的初始情况具有相似的聚类效果
        centers.sort((a, b) => Math.max(...a) - Math.max(...b))

        //计算像素到各中心的距离
        let dists = []
        for (let i = 0; i < k; i++) {
            let tmpdist = disttype == 'dist' ? this.dist(centers[i]) : this.cossimdist(centers[i])
            dists.push(tmpdist)
        }
        //计算聚类结果
        let clusternum = Array(k).fill(0)
        let newcenters = Array(k)
        for (let i = 0; i < k; i++) {
            newcenters[i] = new Array(this.channel).fill(0);
        }

        let clusterresult = new ImgArray({ height: this.height, width: this.width, channel: 1, lazy: false, dtype: 'cuint8' })
        for (let i = 0; i < dists[0].data.length; i++) {
            let minv = Infinity;
            let minidx = 0;
            for (let idx = 0; idx < dists.length; idx++) {
                if (dists[idx].data[i] < minv) {
                    minv = dists[idx].data[i]
                    minidx = idx
                }
            }
            //console.log(minv,minidx)
            clusterresult.data[i] = minidx
            clusternum[minidx] += 1  //类别计数器加1
            let [h, w, c] = dists[0].elidxtoidx(i)
            let idx = this.idxtoelidx(h, w, 0)
            let v2 = this.data.slice(idx, idx + this.channel)
            for (let idx = 0; idx < this.channel; idx++) {
                newcenters[minidx][idx] = v2[idx] + newcenters[minidx][idx]
            }

        }

        //更新聚类中心
        for (let idx = 0; idx < centers.length; idx++) {
            for (let i = 0; i < this.channel; i++) {
                newcenters[idx][i] = newcenters[idx][i] / clusternum[idx];
            }
        }
        return [newcenters, clusterresult]
    }

    /**
     * totensor,将数组转为onnxruntime中的张量。
     * @param {string} [dtype='float32'] - 张量数据类型,可以是 'float32' 或 'uint8'。
     * @returns {ort.Tensor} 返回一个张量对象。
     */
    totensor(dtype = 'float32') {
        if (typeof ort == 'undefined') console.error('需要加载onnnxruntime.js')
        if (dtype == 'float32')
            return new ort.Tensor(dtype, this.data.slice(), this.shape)
        if (dtype == 'uint8') {
            let imgardata = new Uint8Array(this.data)
            return new ort.Tensor(dtype, imgardata, this.shape)
        }
    }

    /**
     * fromtensor,将onnxruntime中的张量转为数组。
     * @static
     * @param {ort.Tensor} tensor - 张量对象。
     * @returns {ImgArray} 返回一个 ImgArray 对象。
     */
    static fromtensor(tensor) {
        let dim = tensor.dims.length;
        let arr = null;
        if (dim == 3)
            arr = new ImgArray(t.dims[0], t.dims[1], t.dims[2]);
        else
            arr = new ImgArray(t.dims[0], t.dims[1], 1);
        arr.data = new Float32Array(t.data);
        return arr;
    }

    /**
     * hwc2chw,用于将hxwxc转为cxhxw的方法,返回一个排列为cxhxw的一维float32数组
     * @returns {ImgArray} 返回一个形状为 CHW 格式的 ImgArray 对象。
     */
    hwc2chw() {
        let chs = this.dsplit();
        let res = new ImgArray({ height: this.channel, width: this.height, channel: this.width, dtype: this.dtype });
        for (let i = 0; i < chs.length; i++) {
            res.data.set(chs[i].data, i * chs[i].data.length);
        }
        return res;
    }

    /**
     * chw2hwc,将图像或数组的形状从 CHW 转换为 HWC 格式。
     * hwc2chw的逆操作,hwc2chw和chw2hwc都是transpose的简单化
     * @returns {ImgArray} 返回一个形状为 HWC 格式的 ImgArray 对象。
     */
    chw2hwc() {
        let { height, width, channel } = this.shape;
        let [c, h, w] = [height, width, channel];
        let baselen = h * w;
        let res = new ImgArray({ height: h, width: w, channel: c, dtype: this.dtype });
        let offsets = [];
        for (let i = 0; i < c; i++) {
            offsets.push(i * h * w);
        }
        for (let i = 0; i < baselen; i++) {
            let idx = i * c;
            for (let j = 0; j < c; j++) {
                let oidx = offsets[j] + i;
                res.data[idx + j] = this.data[oidx];
            }
        }
        return res;
    }

    /**
     * astype,将数组的元素类型转换为指定的类型,要注意不同类型数值表示范围差异可能带来的精度损失!
     * @param {string} [dtype='float32'] - 要转换的元素类型,变量{@link ImgArray.dtypenames}里的所有类型,如 'float32' , 'uint8'等。
     * @returns {ImgArray} 返回一个具有指定元素类型的 ImgArray 对象。
     */
    astype(dtype = 'float32') {
        if (!ImgArray.dtypes[dtype]) {
            console.error('不支持的dtype类型');
            return;
        }
        let res = this.empty(true);
        res.data = new ImgArray.dtypes[dtype](this.data);
        res.dtype = dtype;
        return res;
    }

    /**
     * show,显示数组的内容,当数组为图像时,直接在网页中显示图像,方便观察。
     * 与matploblib的imshow很相似
     * @param {Object} options - 可选参数对象。
     * @param {number} [options.vmin=0] - 数组的最小值。
     * @param {number} [options.vmax=255] - 数组的最大值。
     * @param {number} [options.cas=null] - 可选的画布对象。
     * @returns {Img} 返回显示的图像对象。
     */
    show({ vmin = 0, vmax = 255, cas = null } = {}) {
        if ([1, 3, 4].includes(this.channel)) {
            let data = this.span(vmin, vmax, 0, 255);
            let img = Img.fromarray(data);
            img.show(cas);
            return img;   //返回显示的图像对象,从而可以调用其方法
        }
        else {
            console.error('array channel is not correct, channel should be 1(gray),3(RGB) or 4(RGBA)')
            console.error('数组的通道数不正确,当通道数是1(灰度), 3(RGB彩色), 或 4(带有透明通道的彩色图像)时才可以显示为图像!')
        }
    }

    

    /**
     * toString,参照Numpy的方式将数组转为字符显示,对于大数组的显示存在问题。
     * @returns {string} 数组的字符串表示
     */
    toString() {
        let str = `shape: [${this.height}, ${this.width}, ${this.channel}]\ndtype: ${this.dtype}\ndata :\n[`;
        for (let h = 0; h < this.height; h++) {
            let block = h ? ' [' : '[';
            for (let w = 0; w < this.width; w++) {
                let line = w ? '  [' : '[';
                for (let c = 0; c < this.channel; c++) {
                    let v = this.getel(h, w, c);
                    line += v.toFixed(2) + ' ';
                }
                if (w < this.width - 1)
                    block += line + ']\n';
                else
                    block += line + ']';
            }
            if (h < this.height - 1)
                str += block + ']\n\n';
            else
                str += block + ']';
        }
        return str + ']';
    }

    /**
     * 在控制台以格式化的字符串打印数组。
     */
    print() {
        console.log(this.toString())
    }

    /**
     * tofile,将数组保存为文件。*实验性质的方法,先解决有没有,后续根据需要再完善*
     * @param {string} [name='download'] - 文件名。
     * @param {string} [type='txt'] - 文件类型,可以是 'txt' 或 'bin'。
     */
    tofile(name = 'download', type = 'bin') {
        let buffer;

        if (type === 'txt') {
            let encoder = new TextEncoder();
            buffer = encoder.encode(this.data.toString());
        } else if (type === 'bin') {
            buffer = this.buffer;
        } else {
            console.error("Unsupported type specified");
            return;
        }

        let blob = new Blob([buffer]);
        let url = URL.createObjectURL(blob);

        const save_link = document.createElement("a");
        save_link.href = url;
        save_link.download = `${name}.${type}`;
        save_link.dataset.downloadurl = [type === 'txt' ? 'text/plain' : 'application/octet-stream', save_link.download, url].join(':');
        save_link.click();
        
        // 释放URL对象
        URL.revokeObjectURL(url);
    }
}

/**
 * ImgArray类是一个用于表示图像的类,继承自ImgArray类。
 * Img 继承自ImgArray类,虽然支持ImgArray的所有方法,但是不保证运行正确,
 * 更主要的是用于图像的存取和简单变换,Img 本身就是图像
 * Img的图像类型只支持RGBA排列一种
 * @extends ImgArray
 */
class Img extends ImgArray {
    /**
     * 创建一个Img对象。
     * @param {Object} options - 可选参数对象。
     * @param {number} [options.height=256] - 图像的高度。
     * @param {number} [options.width=256] - 图像的宽度。
     * @param {boolean} [options.lazy=false] - 是否延迟创建图像数据。
     * @returns {Img} 返回一个Img对象。
     */
    constructor({ height = 256, width = 256, lazy = false } = {}) {
        //调用父类ImgArray完成初始化
        super({ height, width, channel: 4, lazy, dtype: 'cuint8' });
        //创建一个保存图像的canvas
        let cas = document.createElement('canvas');
        this.cas = cas;
        this.cas.height = height;
        this.cas.width = width;
        this.ctx = cas.getContext('2d');
        this.iscasnew = false;  //用于标记图像数据和画布哪个新,要用新的去同步旧的
    }

    /**
     * update,更新图像数据到画布中,根据标志位进行数据和canvas同步。
     */
    update() {
        if (this.iscasnew) {
            let imgdata = this.ctx.getImageData(0, 0, this.cas.width, this.cas.height);
            this.data = imgdata.data.slice();
            this.iscasnew = false;
        }
        else {
            let imgdata = new ImageData(this.data, this.width, this.height)
            this.ctx.putImageData(imgdata, 0, 0)
        }
    }

    /**
     * fromarray,将 ImgArray 对象转换为 Img 对象。
     * 该方法支持灰度图 (channel = 1)、RGB 图 (channel = 3)、RGBA 图 (channel = 4) 的转换。
     * @static
     * @param {ImgArray} imgarray - 包含图像数据的 ImgArray 对象。
     * @param {number} imgarray.height - 图像的高度。
     * @param {number} imgarray.width - 图像的宽度。
     * @param {number} imgarray.channel - 图像的通道数:
     *     - 1 表示灰度图。
     *     - 3 表示 RGB 图。
     *     - 4 表示 RGBA 图。
     * @param {Uint8Array} imgarray.data - 一维数组,存储图像的像素值。
     *     - 若 `channel = 1`,数组长度为 height * width。
     *     - 若 `channel = 3`,数组长度为 height * width * 3。
     *     - 若 `channel = 4`,数组长度为 height * width * 4。
     * @returns {Img|null} - 转换后的 Img 对象(包含 RGBA 格式的像素值),或在不支持的通道数时返回 `null`。
     * @throws {Error} 如果 `imgarray.channel` 不是 1、3 或 4,则打印错误信息。
     */
    static fromarray(imgarray) {
        let img = null;
        let pixelnum = imgarray.data.length / imgarray.channel;
        if (imgarray.channel == 1) {
            img = new Img({ height: imgarray.height, width: imgarray.width });
            for (let i = 0; i < pixelnum; i += 1) {
                img.data[i * 4] = imgarray.data[i];
                img.data[i * 4 + 1] = imgarray.data[i];
                img.data[i * 4 + 2] = imgarray.data[i];
                img.data[i * 4 + 3] = 255;//不透明
            }
        } else if (imgarray.channel == 3) {
            img = new Img({ height: imgarray.height, width: imgarray.width });
            for (let i = 0; i < pixelnum; i += 1) {
                img.data[i * 4] = imgarray.data[i * 3];
                img.data[i * 4 + 1] = imgarray.data[i * 3 + 1];
                img.data[i * 4 + 2] = imgarray.data[i * 3 + 2];
                img.data[i * 4 + 3] = 255;
            }
        } else if (imgarray.channel == 4) {
            img = new Img({ height: imgarray.height, width: imgarray.width });
            img.data.set(imgarray.data);
        } else {
            console.error("不支持的通道数:" + imgarray.channel);
        }
        return img;
    }

    /**
     * fromcanvas,从canvas元素生成图像。
     * @static
     * @param {HTMLCanvasElement} canvasele - canvas元素。
     * @returns {Img} - 生成的图像对象。
     */
    static fromcanvas(canvasele) {
        let ctx = canvasele.getContext("2d");
        let imgdata = ctx.getImageData(0, 0, canvasele.width, canvasele.height);
        let oimg = new Img({ height: imgdata.height, width: imgdata.width, lazy: true });
        oimg.data = imgdata.data.slice();  //.slice() 复制数据
        return oimg
    }

    /**
     * fromimg,从图像元素生成图像。
     * @static
     * @param {HTMLImageElement} imgele - 图像元素。
     * @returns {Img} - 生成的图像对象。
     */
    static fromimg(imgele) {
        if (!imgele.width) {
            console.error('no image');
            return null;
        }
        let width = imgele.width
        let height = imgele.height
        let img = new Img({ width, height, lazy: true })
        img.ctx.drawImage(imgele, 0, 0, img.width, img.height);
        img.iscasnew = true;
        img.update()
        return img
    }
    /**
     * fromurl,从图像链接生成图像。
     * @static
     * @param {string} imgurl - 图像链接。
     * @returns {Promise<Img>} - 生成的图像对象,需要使用await。
     */
    static fromurl(imgurl) {
        return new Promise(function (r, e) {
            let img = new Image();
            img.src = imgurl;
            img.setAttribute('crossOrigin', '');//解决网络图片跨域的问题
            img.onload = (ex) => {
                r(Img.fromimg(img));
            }
            img.onerror = (ex) => { return e(ex) }
        })
    }

    /**
     * fromblob,从blob对象生成图像。
     * @static
     * @param {Blob} blob - blob对象。
     * @returns {Promise<Img>} - 生成的图像对象。
     */
    static fromblob(blob) {
        //返回是个promise对象,需要使用await
        //从编码的图像中生成图像,即将blob对象转为Image
        //测试代码:
        //fetch('/a.jpg').then(x=>{ return x.blob()}).then(b=>{return Img.fromblob(b)}).then(img=>{img.tocanvas(cas)})
        let src = URL.createObjectURL(blob)
        return this.fromurl(src)
    }

    /**
     * fromfile,从本地文件生成图像。
     * @static
     * @param {File} fileobj - 本地文件对象。
     * @returns {Promise<Img>} - 生成的图像对象。
     */
    static fromfile(fileobj) {
        //从本地文件生成图像
        /*
        使用方法
        html:
        <input type="file" id="fileInput">
        js:
        var finput=document.querySelector('#fileInput')
            finput.addEventListener('change',()=>{
                console.log(finput.files)
                let fileobj=finput.files[0]
                console.log(fileobj)
                Img.fromfile(fileobj).then((img)=>{
                    img.tocanvas(cas)})
            })
        */
        return new Promise(function (r, e) {
            const reader = new FileReader()
            // console.log(fileobj.type)
            if (!fileobj.type.startsWith('image')) return e('not image')
            reader.readAsDataURL(fileobj)

            reader.onload = () => {
                let url = reader.result
                r(Img.fromurl(url))
            }
        })
    }

    /**
     * fromvideo,从视频流生成图像。
     * @static
     * @param {HTMLVideoElement} videoele - 视频元素。
     * @param {number} [h] - 输出图像的高度。
     * @param {number} [w] - 输出图像的宽度。
     * @returns {Img} - 生成的图像对象。
     */
    static fromvideo(videoele, h = null, w = null) {
        //从video的视频流中截取图像
        let vcas = document.createElement('canvas');
        vcas.width = w ? w : videoele.videoWidth;
        vcas.height = h ? h : videoele.videoHeight;
        let context = vcas.getContext('2d');
        context.drawImage(videoele, 0, 0);
        return Img.fromcanvas(vcas);
    }

    /**
     * videotocanvas,在canvas element 上显示画布。
     * @static
     * @param {HTMLCanvasElement} canvasel - canvas元素。
     */
    static videotocanvas(canvasel) {
        //在canvas element 上显示画布
        let context = canvasel.getContext('2d');

        navigator.mediaDevices.getUserMedia({ video: true })
            .then(function (stream) {
                var video = document.createElement('video');
                video.srcObject = stream;
                video.play();
                video.addEventListener('loadedmetadata', function () {
                    canvasel.width = video.videoWidth;
                    canvasel.height = video.videoHeight;
                });

                function drawFrame() {
                    context.drawImage(video, 0, 0);
                    requestAnimationFrame(drawFrame);
                }
                requestAnimationFrame(drawFrame);
            })
            .catch(function (error) {
                console.log('无法获取视频流: ', error);
            });
    }

    /**
     * fromcamera,从摄像头头获取一幅图像。
     * @static
     * @param {number} [h] - 输出图像的高度。
     * @param {number} [w] - 输出图像的宽度。
     * @returns {Promise<Img>} - 生成的图像对象。
     */
    static fromcamera(h = null, w = null) {
        //从摄像头头获取一幅图像
        //返回是个promise对象,需要使用await
        return new Promise(function (r, e) {
            try {
                navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {

                    let video = document.createElement('video');
                    video.srcObject = stream;
                    video.play();
                    video.addEventListener('loadeddata', (e) => {
                        r(Img.fromvideo(video, h, w))
                    })
                });

            }
            catch (error) {
                e('打开相机失败!');
            }
        })
    }


    /**
     * tofile,将图像保存为文件。
     * @param {string} [name = 'download'] - 文件名。
     */
    tofile(name = 'download') {
        //将图像以指定名称保存,只支持png格式
        const save_link = document.createElement("a");
        save_link.href = this.tourl();
        save_link.download = name + '.png';
        save_link.dataset.downloadurl = ["image/png", save_link.download, save_link.href].join(':');
        save_link.click();
    }

    /**
     * tourl,将图像转为url。
     * @returns {string} - url
     */
    tourl() {
        //将图像转为url
        if (!this.iscasnew) this.update();
        return this.cas.toDataURL('image/png', 1);//图像不会被压缩
    }

    /**
     * toimg,将图像转为img标签元素在网页中展示出来。
     * @param {HTMLImageElement} imgele - img标签元素。
     */
    toimg(imgele) {
        //将Img转换为img标签元素在网页中展示出来
        let data = this.tourl()
        imgele.src = data
    }

    /**
     * tocanvas,将图像转为canvas标签元素在网页中展示出来。
     * @param {HTMLCanvasElement} canvasele - canvas标签元素。
     * @param {boolean} [isfull = false] - 是否使canvas适配图像。
     */
    tocanvas(canvasele, isfull = false) {
        if (this.iscasnew) this.update();
        //将Img转换为画布元素在网页中展示出来,需要canvasele的宽高与图像一致
        //需要使canvas适配图像的话将isfull置true
        if (isfull) {
            canvasele.width = this.width;
            canvasele.height = this.height;
        }
        let ctx = canvasele.getContext("2d");
        let imgdata = new ImageData(this.data, this.width, this.height)
        ctx.putImageData(imgdata, 0, 0)
    }

    /**
     * toarray,将图像转为数组。
     * @param {boolean} [droptrans = true] - 是否丢弃通道信息。
     * @returns {ImgArray} - 生成的数组对象。
     */
    toarray(droptrans = true) {
        //将图像转为数组,droptrans表示通道丢弃,即只保留RGB三个通道的数据
        if (this.iscasnew) this.update();
        let channel = droptrans ? 3 : 4;
        if (droptrans) {
            let oimgar = new ImgArray({ height: this.height, width: this.width, channel });
            oimgar.data.forEach((x, idx, arr) => { arr[idx] = this.data[parseInt(idx / 3) * 4 + idx % 3] }, this);
            return oimgar;
        }
        else {
            let oimgar = new ImgArray({ height: this.height, width: this.width, channel, lazy: true });
            oimgar.data = new Float32Array(this.data);
            return oimgar;
        }
    }

    /**
     * hist,获取直方图。
     * @param {number} [channel = 3] - 通道数。
     * @returns {Uint32Array|Array<Uint32Array>} - 生成的直方图。
     */
    hist(channel = 3) {
        //函数接收通道数(0,1,2),输入3时表示三通道的直方图
        if (typeof channel !== 'number' || channel < 0 || channel > 3) {
            throw new TypeError('Invalid channel parameter');
        }
        if (channel === 3) {
            let histList = [
                new Uint32Array(256),
                new Uint32Array(256),
                new Uint32Array(256)
            ];
            this.data.forEach(function (x, idx) {
                let c = idx % 4;
                if (c !== 3) {
                    histList[c][x]++;
                }
            });
            return histList;
        } else {
            let hist = new Uint32Array(256);
            this.data.forEach(function (x, idx) {
                if (idx % 4 === channel) {
                    if (hist[x] === undefined) {
                        hist[x] = 0;
                    }
                    hist[x]++;
                }
            });
            return hist;
        }
    }

    /**
     * getpixel,获取图像的像素值
     * @param {number} x - 像素的x坐标,横坐标
     * @param {number} y - 像素的y坐标,纵坐标
     * @returns {Uint8ClampedArray(4)} rgba,范围为0~255
     */
    getpixel(x, y) {
        let idx = this.idxtoelidx(y, x, 0);
        return this.data.slice(idx, idx + this.channel);
    }

    /**
     * setpixel,设置图像的像素值
     * @param {number} x - 像素的x坐标,横坐标
     * @param {number} y - 像素的y坐标,纵坐标
     * @param {number[]} [rgba = [0, 0, 0, 255]] ,元素值的范围要为0~255
     */
    setpixel(x, y, rgba = [0, 0, 0, 255]) {
        rgba = new Uint8ClampedArray(rgba);
        if (rgba.length > 4)
            rgba = rgba.slice(0, 4);
        let idx = this.idxtoelidx(y, x, 0);
        this.data.set(rgba, idx);
    }

    /**
     * gray(method='mean'),对图像进行灰度处理。
     *
     * @param method 灰度化方法,可选值为 'mean' 或 'max'。默认为 'mean'。
     *                 - 'mean':通过计算像素值的平均值来进行灰度化。
     *                 - 'max':通过取像素值的最大值来进行灰度化。
     * @returns {Img} 灰度化后的图像。
     */
    gray(method='mean'){
        let arr=this.toarray();
        if (method=='mean')
            return Img.fromarray(arr.mean(true));
        return Img.fromarray(arr.max(true))
    }

    /**
     * COLORMAPNAMES,获取所有的颜色映射名称,作为{@link Img#applyColorMap}的参数使用。
     *
     * @returns 返回包含所有颜色映射名称的数组
     */
    static get COLORMAPNAMES() {
        return Object.keys(Img.COLORMAPS);
    }

    /**
     * COLORMAPS,伪彩色映射表,与OpenCV中的值一致。
     * @static
     * @type {Object}
     */
    static COLORMAPS ={
        "AUTUMN": [[0, 0, 255], [0, 1, 255], [0, 2, 255], [0, 3, 255], [0, 4, 255], [0, 5, 255], [0, 6, 255], [0, 7, 255], [0, 8, 255], [0, 9, 255], [0, 10, 255], [0, 11, 255], [0, 12, 255], [0, 13, 255], [0, 14, 255], [0, 15, 255], [0, 16, 255], [0, 17, 255], [0, 18, 255], [0, 19, 255], [0, 20, 255], [0, 21, 255], [0, 22, 255], [0, 23, 255], [0, 24, 255], [0, 25, 255], [0, 26, 255], [0, 27, 255], [0, 28, 255], [0, 29, 255], [0, 30, 255], [0, 31, 255], [0, 32, 255], [0, 33, 255], [0, 34, 255], [0, 35, 255], [0, 36, 255], [0, 37, 255], [0, 38, 255], [0, 39, 255], [0, 40, 255], [0, 41, 255], [0, 42, 255], [0, 43, 255], [0, 44, 255], [0, 45, 255], [0, 46, 255], [0, 47, 255], [0, 48, 255], [0, 49, 255], [0, 50, 255], [0, 51, 255], [0, 52, 255], [0, 53, 255], [0, 54, 255], [0, 55, 255], [0, 56, 255], [0, 57, 255], [0, 58, 255], [0, 59, 255], [0, 60, 255], [0, 61, 255], [0, 62, 255], [0, 63, 255], [0, 64, 255], [0, 65, 255], [0, 66, 255], [0, 67, 255], [0, 68, 255], [0, 69, 255], [0, 70, 255], [0, 71, 255], [0, 72, 255], [0, 73, 255], [0, 74, 255], [0, 75, 255], [0, 76, 255], [0, 77, 255], [0, 78, 255], [0, 79, 255], [0, 80, 255], [0, 81, 255], [0, 82, 255], [0, 83, 255], [0, 84, 255], [0, 85, 255], [0, 86, 255], [0, 87, 255], [0, 88, 255], [0, 89, 255], [0, 90, 255], [0, 91, 255], [0, 92, 255], [0, 93, 255], [0, 94, 255], [0, 95, 255], [0, 96, 255], [0, 97, 255], [0, 98, 255], [0, 99, 255], [0, 100, 255], [0, 101, 255], [0, 102, 255], [0, 103, 255], [0, 104, 255], [0, 105, 255], [0, 106, 255], [0, 107, 255], [0, 108, 255], [0, 109, 255], [0, 110, 255], [0, 111, 255], [0, 112, 255], [0, 113, 255], [0, 114, 255], [0, 115, 255], [0, 116, 255], [0, 117, 255], [0, 118, 255], [0, 119, 255], [0, 120, 255], [0, 121, 255], [0, 122, 255], [0, 123, 255], [0, 124, 255], [0, 125, 255], [0, 126, 255], [0, 127, 255], [0, 128, 255], [0, 129, 255], [0, 130, 255], [0, 131, 255], [0, 132, 255], [0, 133, 255], [0, 134, 255], [0, 135, 255], [0, 136, 255], [0, 137, 255], [0, 138, 255], [0, 139, 255], [0, 140, 255], [0, 141, 255], [0, 142, 255], [0, 143, 255], [0, 144, 255], [0, 145, 255], [0, 146, 255], [0, 147, 255], [0, 148, 255], [0, 149, 255], [0, 150, 255], [0, 151, 255], [0, 152, 255], [0, 153, 255], [0, 154, 255], [0, 155, 255], [0, 156, 255], [0, 157, 255], [0, 158, 255], [0, 159, 255], [0, 160, 255], [0, 161, 255], [0, 162, 255], [0, 163, 255], [0, 164, 255], [0, 165, 255], [0, 166, 255], [0, 167, 255], [0, 168, 255], [0, 169, 255], [0, 170, 255], [0, 171, 255], [0, 172, 255], [0, 173, 255], [0, 174, 255], [0, 175, 255], [0, 176, 255], [0, 177, 255], [0, 178, 255], [0, 179, 255], [0, 180, 255], [0, 181, 255], [0, 182, 255], [0, 183, 255], [0, 184, 255], [0, 185, 255], [0, 186, 255], [0, 187, 255], [0, 188, 255], [0, 189, 255], [0, 190, 255], [0, 191, 255], [0, 192, 255], [0, 193, 255], [0, 194, 255], [0, 195, 255], [0, 196, 255], [0, 197, 255], [0, 198, 255], [0, 199, 255], [0, 200, 255], [0, 201, 255], [0, 202, 255], [0, 203, 255], [0, 204, 255], [0, 205, 255], [0, 206, 255], [0, 207, 255], [0, 208, 255], [0, 209, 255], [0, 210, 255], [0, 211, 255], [0, 212, 255], [0, 213, 255], [0, 214, 255], [0, 215, 255], [0, 216, 255], [0, 217, 255], [0, 218, 255], [0, 219, 255], [0, 220, 255], [0, 221, 255], [0, 222, 255], [0, 223, 255], [0, 224, 255], [0, 225, 255], [0, 226, 255], [0, 227, 255], [0, 228, 255], [0, 229, 255], [0, 230, 255], [0, 231, 255], [0, 232, 255], [0, 233, 255], [0, 234, 255], [0, 235, 255], [0, 236, 255], [0, 237, 255], [0, 238, 255], [0, 239, 255], [0, 240, 255], [0, 241, 255], [0, 242, 255], [0, 243, 255], [0, 244, 255], [0, 245, 255], [0, 246, 255], [0, 247, 255], [0, 248, 255], [0, 249, 255], [0, 250, 255], [0, 251, 255], [0, 252, 255], [0, 253, 255], [0, 254, 255], [0, 255, 255]], "BONE": [[0, 0, 0], [1, 1, 1], [2, 2, 2], [4, 3, 3], [5, 4, 4], [6, 4, 4], [7, 5, 5], [8, 6, 6], [10, 7, 7], [11, 8, 8], [12, 9, 9], [13, 10, 10], [14, 10, 10], [16, 11, 11], [17, 12, 12], [18, 13, 13], [19, 14, 14], [21, 15, 15], [22, 16, 16], [23, 17, 17], [24, 18, 18], [25, 18, 18], [27, 19, 19], [28, 20, 20], [29, 21, 21], [30, 22, 22], [31, 23, 23], [33, 24, 24], [34, 24, 24], [35, 25, 25], [36, 26, 26], [37, 27, 27], [39, 28, 28], [40, 29, 29], [41, 30, 30], [42, 31, 31], [43, 32, 32], [45, 32, 32], [46, 33, 33], [47, 34, 34], [48, 35, 35], [50, 36, 36], [51, 37, 37], [52, 38, 38], [53, 38, 38], [54, 39, 39], [56, 40, 40], [57, 41, 41], [58, 42, 42], [59, 43, 43], [60, 44, 44], [62, 45, 45], [63, 46, 46], [64, 46, 46], [65, 47, 47], [66, 48, 48], [68, 49, 49], [69, 50, 50], [70, 51, 51], [71, 52, 52], [72, 52, 52], [74, 53, 53], [75, 54, 54], [76, 55, 55], [77, 56, 56], [79, 57, 57], [80, 58, 58], [81, 59, 59], [82, 60, 60], [83, 60, 60], [85, 61, 61], [86, 62, 62], [87, 63, 63], [88, 64, 64], [89, 65, 65], [91, 66, 66], [92, 66, 66], [93, 67, 67], [94, 68, 68], [95, 69, 69], [97, 70, 70], [98, 71, 71], [99, 72, 72], [100, 73, 73], [102, 73, 73], [103, 74, 74], [104, 75, 75], [105, 76, 76], [106, 77, 77], [108, 78, 78], [109, 79, 79], [110, 80, 80], [111, 80, 80], [112, 81, 81], [113, 82, 82], [115, 83, 83], [116, 84, 84], [117, 85, 85], [118, 87, 86], [118, 88, 87], [119, 89, 87], [120, 90, 88], [121, 91, 89], [122, 93, 90], [123, 94, 91], [124, 95, 92], [125, 96, 93], [126, 97, 94], [126, 99, 94], [127, 100, 95], [128, 101, 96], [129, 102, 97], [130, 103, 98], [131, 105, 99], [132, 106, 100], [132, 107, 101], [133, 108, 102], [134, 110, 102], [135, 111, 103], [136, 112, 104], [137, 113, 105], [138, 114, 106], [139, 116, 107], [140, 117, 108], [140, 118, 108], [141, 119, 109], [142, 120, 110], [143, 122, 111], [144, 123, 112], [145, 124, 113], [146, 125, 114], [146, 126, 115], [147, 128, 116], [148, 129, 116], [149, 130, 117], [150, 131, 118], [151, 132, 119], [152, 134, 120], [153, 135, 121], [154, 136, 122], [154, 137, 122], [155, 138, 123], [156, 140, 124], [157, 141, 125], [158, 142, 126], [159, 143, 127], [160, 145, 128], [160, 146, 129], [161, 147, 130], [162, 148, 130], [163, 149, 131], [164, 151, 132], [165, 152, 133], [166, 153, 134], [167, 154, 135], [168, 155, 136], [168, 157, 136], [169, 158, 137], [170, 159, 138], [171, 160, 139], [172, 161, 140], [173, 163, 141], [174, 164, 142], [174, 165, 143], [175, 166, 144], [176, 168, 144], [177, 169, 145], [178, 170, 146], [179, 171, 147], [180, 172, 148], [181, 174, 149], [182, 175, 150], [182, 176, 150], [183, 177, 151], [184, 178, 152], [185, 180, 153], [186, 181, 154], [187, 182, 155], [188, 183, 156], [188, 184, 157], [189, 186, 158], [190, 187, 158], [191, 188, 159], [192, 189, 160], [193, 190, 161], [194, 192, 162], [195, 193, 163], [196, 194, 164], [196, 195, 164], [197, 196, 165], [198, 198, 166], [199, 199, 167], [200, 200, 169], [201, 201, 170], [202, 202, 171], [203, 203, 172], [203, 203, 174], [204, 204, 175], [205, 205, 177], [206, 206, 178], [207, 207, 179], [208, 208, 181], [209, 209, 182], [210, 210, 184], [210, 210, 185], [211, 211, 186], [212, 212, 188], [213, 213, 189], [214, 214, 190], [215, 215, 192], [216, 216, 193], [216, 216, 195], [217, 217, 196], [218, 218, 197], [219, 219, 199], [220, 220, 200], [221, 221, 201], [222, 222, 203], [223, 223, 204], [224, 224, 206], [224, 224, 207], [225, 225, 208], [226, 226, 210], [227, 227, 211], [228, 228, 212], [229, 229, 214], [230, 230, 215], [231, 231, 216], [231, 231, 218], [232, 232, 219], [233, 233, 221], [234, 234, 222], [235, 235, 223], [236, 236, 225], [237, 237, 226], [238, 238, 228], [238, 238, 229], [239, 239, 230], [240, 240, 232], [241, 241, 233], [242, 242, 234], [243, 243, 236], [244, 244, 237], [244, 244, 239], [245, 245, 240], [246, 246, 241], [247, 247, 243], [248, 248, 244], [249, 249, 245], [250, 250, 247], [251, 251, 248], [252, 252, 250], [252, 252, 251], [253, 253, 252], [254, 254, 254], [255, 255, 255]], "CIVIDIS": [[78, 34, 0], [79, 35, 0], [81, 36, 0], [83, 37, 0], [84, 37, 0], [86, 38, 0], [88, 39, 0], [89, 40, 0], [91, 40, 0], [93, 41, 0], [95, 42, 0], [97, 42, 0], [98, 43, 0], [100, 44, 0], [102, 44, 0], [104, 45, 0], [106, 46, 0], [108, 46, 0], [109, 47, 0], [111, 48, 0], [112, 48, 0], [112, 49, 0], [113, 49, 0], [113, 50, 1], [113, 51, 5], [112, 51, 8], [112, 52, 12], [112, 53, 15], [112, 53, 18], [112, 54, 20], [112, 55, 22], [111, 55, 24], [111, 56, 26], [111, 57, 28], [111, 58, 30], [111, 58, 32], [110, 59, 33], [110, 60, 35], [110, 60, 36], [110, 61, 38], [110, 62, 39], [110, 63, 41], [109, 63, 42], [109, 64, 43], [109, 65, 45], [109, 65, 46], [109, 66, 47], [109, 67, 49], [109, 67, 50], [109, 68, 51], [108, 69, 52], [108, 69, 53], [108, 70, 54], [108, 71, 56], [108, 72, 57], [108, 72, 58], [108, 73, 59], [108, 74, 60], [108, 74, 61], [108, 75, 62], [108, 76, 63], [108, 76, 64], [108, 77, 65], [108, 78, 66], [108, 78, 67], [108, 79, 68], [108, 80, 69], [108, 81, 70], [108, 81, 71], [108, 82, 72], [108, 83, 73], [108, 83, 74], [108, 84, 75], [108, 85, 76], [108, 85, 77], [108, 86, 78], [108, 87, 79], [108, 87, 80], [109, 88, 81], [109, 89, 82], [109, 90, 83], [109, 90, 84], [109, 91, 85], [109, 92, 85], [109, 92, 86], [109, 93, 87], [109, 94, 88], [110, 94, 89], [110, 95, 90], [110, 96, 91], [110, 97, 92], [110, 97, 93], [110, 98, 94], [111, 99, 94], [111, 99, 95], [111, 100, 96], [111, 101, 97], [111, 101, 98], [112, 102, 99], [112, 103, 100], [112, 104, 101], [112, 104, 101], [112, 105, 102], [113, 106, 103], [113, 106, 104], [113, 107, 105], [113, 108, 106], [114, 109, 107], [114, 109, 108], [114, 110, 108], [114, 111, 109], [115, 111, 110], [115, 112, 111], [115, 113, 112], [116, 114, 113], [116, 114, 114], [116, 115, 114], [117, 116, 115], [117, 116, 116], [117, 117, 117], [118, 118, 118], [118, 119, 119], [119, 119, 119], [119, 120, 120], [119, 121, 121], [120, 122, 122], [120, 122, 123], [120, 123, 124], [120, 124, 125], [120, 124, 126], [120, 125, 126], [120, 126, 127], [120, 127, 128], [120, 127, 129], [121, 128, 130], [121, 129, 131], [121, 130, 132], [121, 130, 133], [121, 131, 134], [120, 132, 135], [120, 133, 136], [120, 133, 137], [120, 134, 138], [120, 135, 139], [120, 136, 140], [120, 136, 141], [120, 137, 142], [120, 138, 143], [120, 139, 144], [120, 139, 145], [120, 140, 146], [120, 141, 146], [120, 142, 147], [119, 142, 148], [119, 143, 149], [119, 144, 150], [119, 145, 151], [119, 146, 152], [119, 146, 153], [118, 147, 154], [118, 148, 155], [118, 149, 156], [118, 149, 157], [118, 150, 158], [117, 151, 159], [117, 152, 160], [117, 153, 161], [117, 153, 162], [116, 154, 163], [116, 155, 164], [116, 156, 165], [116, 156, 166], [115, 157, 167], [115, 158, 168], [115, 159, 169], [115, 160, 170], [114, 160, 171], [114, 161, 172], [114, 162, 173], [113, 163, 174], [113, 164, 175], [113, 165, 176], [112, 165, 177], [112, 166, 179], [111, 167, 180], [111, 168, 181], [111, 169, 182], [110, 169, 183], [110, 170, 184], [109, 171, 185], [109, 172, 186], [109, 173, 187], [108, 174, 188], [108, 174, 189], [107, 175, 190], [107, 176, 191], [106, 177, 192], [106, 178, 193], [105, 179, 194], [105, 179, 195], [104, 180, 196], [104, 181, 197], [103, 182, 198], [103, 183, 199], [102, 184, 200], [101, 185, 201], [101, 185, 203], [100, 186, 204], [99, 187, 205], [99, 188, 206], [98, 189, 207], [98, 190, 208], [97, 191, 209], [96, 192, 210], [95, 192, 211], [95, 193, 212], [94, 194, 213], [93, 195, 214], [92, 196, 215], [92, 197, 217], [91, 198, 218], [90, 199, 219], [89, 200, 220], [88, 200, 221], [88, 201, 222], [87, 202, 223], [86, 203, 224], [85, 204, 225], [84, 205, 226], [83, 206, 228], [82, 207, 229], [81, 208, 230], [80, 209, 231], [79, 210, 232], [78, 211, 233], [76, 211, 234], [75, 212, 235], [74, 213, 237], [73, 214, 238], [72, 215, 239], [70, 216, 240], [69, 217, 241], [68, 218, 242], [66, 219, 243], [65, 220, 245], [63, 221, 246], [62, 222, 247], [60, 223, 248], [58, 224, 249], [56, 225, 251], [54, 226, 252], [52, 227, 253], [52, 228, 254], [53, 229, 254], [54, 230, 254], [56, 232, 254]], "COOL": [[255, 255, 0], [255, 254, 1], [255, 253, 2], [255, 252, 3], [255, 251, 4], [255, 250, 5], [255, 249, 6], [255, 248, 7], [255, 247, 8], [255, 246, 9], [255, 245, 10], [255, 244, 11], [255, 243, 12], [255, 242, 13], [255, 241, 14], [255, 240, 15], [255, 239, 16], [255, 238, 17], [255, 237, 18], [255, 236, 19], [255, 235, 20], [255, 234, 21], [255, 233, 22], [255, 232, 23], [255, 231, 24], [255, 230, 25], [255, 229, 26], [255, 228, 27], [255, 227, 28], [255, 226, 29], [255, 225, 30], [255, 224, 31], [255, 223, 32], [255, 222, 33], [255, 221, 34], [255, 220, 35], [255, 219, 36], [255, 218, 37], [255, 217, 38], [255, 216, 39], [255, 215, 40], [255, 214, 41], [255, 213, 42], [255, 212, 43], [255, 211, 44], [255, 210, 45], [255, 209, 46], [255, 208, 47], [255, 207, 48], [255, 206, 49], [255, 205, 50], [255, 204, 51], [255, 203, 52], [255, 202, 53], [255, 201, 54], [255, 200, 55], [255, 199, 56], [255, 198, 57], [255, 197, 58], [255, 196, 59], [255, 195, 60], [255, 194, 61], [255, 193, 62], [255, 192, 63], [255, 191, 64], [255, 190, 65], [255, 189, 66], [255, 188, 67], [255, 187, 68], [255, 186, 69], [255, 185, 70], [255, 184, 71], [255, 183, 72], [255, 182, 73], [255, 181, 74], [255, 180, 75], [255, 179, 76], [255, 178, 77], [255, 177, 78], [255, 176, 79], [255, 175, 80], [255, 174, 81], [255, 173, 82], [255, 172, 83], [255, 171, 84], [255, 170, 85], [255, 169, 86], [255, 168, 87], [255, 167, 88], [255, 166, 89], [255, 165, 90], [255, 164, 91], [255, 163, 92], [255, 162, 93], [255, 161, 94], [255, 160, 95], [255, 159, 96], [255, 158, 97], [255, 157, 98], [255, 156, 99], [255, 155, 100], [255, 154, 101], [255, 153, 102], [255, 152, 103], [255, 151, 104], [255, 150, 105], [255, 149, 106], [255, 148, 107], [255, 147, 108], [255, 146, 109], [255, 145, 110], [255, 144, 111], [255, 143, 112], [255, 142, 113], [255, 141, 114], [255, 140, 115], [255, 139, 116], [255, 138, 117], [255, 137, 118], [255, 136, 119], [255, 135, 120], [255, 134, 121], [255, 133, 122], [255, 132, 123], [255, 131, 124], [255, 130, 125], [255, 129, 126], [255, 128, 127], [255, 127, 128], [255, 126, 129], [255, 125, 130], [255, 124, 131], [255, 123, 132], [255, 122, 133], [255, 121, 134], [255, 120, 135], [255, 119, 136], [255, 118, 137], [255, 117, 138], [255, 116, 139], [255, 115, 140], [255, 114, 141], [255, 113, 142], [255, 112, 143], [255, 111, 144], [255, 110, 145], [255, 109, 146], [255, 108, 147], [255, 107, 148], [255, 106, 149], [255, 105, 150], [255, 104, 151], [255, 103, 152], [255, 102, 153], [255, 101, 154], [255, 100, 155], [255, 99, 156], [255, 98, 157], [255, 97, 158], [255, 96, 159], [255, 95, 160], [255, 94, 161], [255, 93, 162], [255, 92, 163], [255, 91, 164], [255, 90, 165], [255, 89, 166], [255, 88, 167], [255, 87, 168], [255, 86, 169], [255, 85, 170], [255, 84, 171], [255, 83, 172], [255, 82, 173], [255, 81, 174], [255, 80, 175], [255, 79, 176], [255, 78, 177], [255, 77, 178], [255, 76, 179], [255, 75, 180], [255, 74, 181], [255, 73, 182], [255, 72, 183], [255, 71, 184], [255, 70, 185], [255, 69, 186], [255, 68, 187], [255, 67, 188], [255, 66, 189], [255, 65, 190], [255, 64, 191], [255, 63, 192], [255, 62, 193], [255, 61, 194], [255, 60, 195], [255, 59, 196], [255, 58, 197], [255, 57, 198], [255, 56, 199], [255, 55, 200], [255, 54, 201], [255, 53, 202], [255, 52, 203], [255, 51, 204], [255, 50, 205], [255, 49, 206], [255, 48, 207], [255, 47, 208], [255, 46, 209], [255, 45, 210], [255, 44, 211], [255, 43, 212], [255, 42, 213], [255, 41, 214], [255, 40, 215], [255, 39, 216], [255, 38, 217], [255, 37, 218], [255, 36, 219], [255, 35, 220], [255, 34, 221], [255, 33, 222], [255, 32, 223], [255, 31, 224], [255, 30, 225], [255, 29, 226], [255, 28, 227], [255, 27, 228], [255, 26, 229], [255, 25, 230], [255, 24, 231], [255, 23, 232], [255, 22, 233], [255, 21, 234], [255, 20, 235], [255, 19, 236], [255, 18, 237], [255, 17, 238], [255, 16, 239], [255, 15, 240], [255, 14, 241], [255, 13, 242], [255, 12, 243], [255, 11, 244], [255, 10, 245], [255, 9, 246], [255, 8, 247], [255, 7, 248], [255, 6, 249], [255, 5, 250], [255, 4, 251], [255, 3, 252], [255, 2, 253], [255, 1, 254], [255, 0, 255]], "DEEPGREEN": [[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 0], [0, 12, 0], [0, 13, 0], [0, 14, 0], [0, 15, 0], [0, 16, 0], [0, 17, 0], [0, 18, 0], [0, 19, 0], [0, 20, 0], [0, 21, 0], [0, 22, 0], [0, 23, 0], [0, 24, 0], [0, 25, 0], [0, 26, 0], [0, 27, 0], [0, 28, 0], [0, 29, 0], [0, 30, 0], [0, 31, 0], [0, 32, 0], [0, 33, 0], [0, 34, 0], [0, 35, 0], [0, 36, 0], [0, 37, 0], [0, 38, 0], [0, 39, 0], [0, 40, 0], [0, 41, 0], [0, 42, 0], [0, 43, 0], [0, 44, 0], [0, 45, 0], [0, 46, 0], [0, 47, 0], [0, 48, 0], [0, 49, 0], [0, 50, 0], [0, 51, 0], [0, 52, 0], [0, 53, 0], [0, 54, 0], [0, 55, 0], [0, 56, 0], [0, 57, 0], [0, 58, 0], [0, 59, 0], [0, 60, 0], [0, 61, 0], [0, 62, 0], [0, 63, 0], [0, 64, 0], [0, 65, 0], [0, 66, 0], [0, 67, 0], [0, 68, 0], [0, 69, 0], [0, 70, 0], [0, 71, 0], [0, 72, 0], [0, 73, 0], [0, 74, 0], [0, 75, 0], [0, 76, 0], [0, 77, 0], [0, 78, 0], [0, 79, 0], [0, 80, 0], [0, 81, 0], [0, 82, 0], [0, 83, 0], [0, 84, 0], [0, 85, 0], [2, 86, 0], [3, 87, 0], [4, 88, 0], [6, 89, 0], [8, 90, 0], [9, 91, 0], [10, 92, 0], [12, 93, 0], [13, 94, 0], [15, 95, 0], [17, 96, 0], [18, 97, 0], [20, 98, 0], [21, 99, 0], [22, 100, 0], [24, 101, 0], [26, 102, 0], [27, 103, 0], [28, 104, 0], [30, 105, 0], [32, 106, 0], [33, 107, 0], [34, 108, 0], [36, 109, 0], [38, 110, 0], [39, 111, 0], [40, 112, 0], [42, 113, 0], [44, 114, 0], [45, 115, 0], [46, 116, 0], [48, 117, 0], [50, 118, 0], [51, 119, 0], [52, 120, 0], [54, 121, 0], [56, 122, 0], [57, 123, 0], [58, 124, 0], [60, 125, 0], [62, 126, 0], [63, 127, 0], [64, 128, 0], [66, 129, 0], [68, 130, 0], [69, 131, 0], [70, 132, 0], [72, 133, 0], [74, 134, 0], [75, 135, 0], [76, 136, 0], [78, 137, 0], [80, 138, 0], [81, 139, 0], [82, 140, 0], [84, 141, 0], [86, 142, 0], [87, 143, 0], [88, 144, 0], [90, 145, 0], [92, 146, 0], [93, 147, 0], [94, 148, 0], [96, 149, 0], [98, 150, 0], [99, 151, 0], [100, 152, 0], [102, 153, 0], [104, 154, 0], [105, 155, 0], [106, 156, 0], [108, 157, 0], [110, 158, 0], [111, 159, 0], [112, 160, 0], [114, 161, 0], [116, 162, 0], [117, 163, 0], [118, 164, 0], [120, 165, 0], [122, 166, 0], [123, 167, 0], [124, 168, 0], [126, 169, 0], [128, 170, 0], [129, 171, 3], [130, 172, 6], [132, 173, 9], [134, 174, 12], [135, 175, 15], [136, 176, 18], [138, 177, 21], [140, 178, 24], [141, 179, 27], [142, 180, 30], [144, 181, 33], [146, 182, 36], [147, 183, 39], [148, 184, 42], [150, 185, 45], [152, 186, 48], [153, 187, 51], [154, 188, 54], [156, 189, 57], [158, 190, 60], [159, 191, 63], [161, 192, 66], [162, 193, 69], [164, 194, 72], [165, 195, 75], [166, 196, 78], [168, 197, 81], [170, 198, 84], [171, 199, 87], [172, 200, 90], [174, 201, 93], [176, 202, 96], [177, 203, 99], [178, 204, 102], [180, 205, 105], [182, 206, 108], [183, 207, 111], [184, 208, 114], [186, 209, 117], [188, 210, 120], [189, 211, 123], [190, 212, 126], [192, 213, 129], [194, 214, 132], [195, 215, 135], [196, 216, 138], [198, 217, 141], [200, 218, 144], [201, 219, 147], [202, 220, 150], [204, 221, 153], [206, 222, 156], [207, 223, 159], [208, 224, 162], [210, 225, 165], [212, 226, 168], [213, 227, 171], [214, 228, 174], [216, 229, 177], [218, 230, 180], [219, 231, 183], [220, 232, 186], [222, 233, 189], [224, 234, 192], [225, 235, 195], [226, 236, 198], [228, 237, 201], [230, 238, 204], [231, 239, 207], [232, 240, 210], [234, 241, 213], [236, 242, 216], [237, 243, 219], [238, 244, 222], [240, 245, 225], [242, 246, 228], [243, 247, 231], [244, 248, 234], [246, 249, 237], [248, 250, 240], [249, 251, 243], [251, 252, 246], [252, 253, 249], [254, 254, 252], [255, 255, 255]], "HOT": [[0, 0, 0], [0, 0, 2], [0, 0, 5], [0, 0, 8], [0, 0, 10], [0, 0, 12], [0, 0, 15], [0, 0, 18], [0, 0, 20], [0, 0, 22], [0, 0, 25], [0, 0, 27], [0, 0, 30], [0, 0, 32], [0, 0, 35], [0, 0, 38], [0, 0, 40], [0, 0, 42], [0, 0, 45], [0, 0, 48], [0, 0, 50], [0, 0, 52], [0, 0, 55], [0, 0, 57], [0, 0, 60], [0, 0, 62], [0, 0, 65], [0, 0, 68], [0, 0, 70], [0, 0, 72], [0, 0, 75], [0, 0, 78], [0, 0, 80], [0, 0, 82], [0, 0, 85], [0, 0, 88], [0, 0, 90], [0, 0, 92], [0, 0, 95], [0, 0, 98], [0, 0, 100], [0, 0, 102], [0, 0, 105], [0, 0, 108], [0, 0, 110], [0, 0, 112], [0, 0, 115], [0, 0, 117], [0, 0, 120], [0, 0, 122], [0, 0, 125], [0, 0, 128], [0, 0, 130], [0, 0, 132], [0, 0, 135], [0, 0, 138], [0, 0, 140], [0, 0, 142], [0, 0, 145], [0, 0, 148], [0, 0, 150], [0, 0, 152], [0, 0, 155], [0, 0, 158], [0, 0, 160], [0, 0, 162], [0, 0, 165], [0, 0, 168], [0, 0, 170], [0, 0, 172], [0, 0, 175], [0, 0, 178], [0, 0, 180], [0, 0, 182], [0, 0, 185], [0, 0, 188], [0, 0, 190], [0, 0, 192], [0, 0, 195], [0, 0, 198], [0, 0, 200], [0, 0, 202], [0, 0, 205], [0, 0, 208], [0, 0, 210], [0, 0, 212], [0, 0, 215], [0, 0, 218], [0, 0, 220], [0, 0, 223], [0, 0, 225], [0, 0, 228], [0, 0, 230], [0, 0, 232], [0, 0, 235], [0, 0, 238], [0, 0, 240], [0, 0, 243], [0, 0, 245], [0, 0, 248], [0, 0, 250], [0, 0, 252], [0, 2, 253], [0, 4, 254], [0, 6, 254], [0, 8, 255], [0, 10, 255], [0, 13, 255], [0, 15, 255], [0, 18, 255], [0, 20, 255], [0, 22, 255], [0, 25, 255], [0, 28, 255], [0, 30, 255], [0, 32, 255], [0, 35, 255], [0, 38, 255], [0, 40, 255], [0, 42, 255], [0, 45, 255], [0, 48, 255], [0, 50, 255], [0, 52, 255], [0, 55, 255], [0, 58, 255], [0, 60, 255], [0, 62, 255], [0, 65, 255], [0, 68, 255], [0, 70, 255], [0, 72, 255], [0, 75, 255], [0, 78, 255], [0, 80, 255], [0, 82, 255], [0, 85, 255], [0, 88, 255], [0, 90, 255], [0, 92, 255], [0, 95, 255], [0, 98, 255], [0, 100, 255], [0, 102, 255], [0, 105, 255], [0, 108, 255], [0, 110, 255], [0, 112, 255], [0, 115, 255], [0, 118, 255], [0, 120, 255], [0, 122, 255], [0, 125, 255], [0, 128, 255], [0, 130, 255], [0, 132, 255], [0, 135, 255], [0, 138, 255], [0, 140, 255], [0, 142, 255], [0, 145, 255], [0, 148, 255], [0, 150, 255], [0, 152, 255], [0, 155, 255], [0, 158, 255], [0, 160, 255], [0, 162, 255], [0, 165, 255], [0, 168, 255], [0, 170, 255], [0, 172, 255], [0, 175, 255], [0, 178, 255], [0, 180, 255], [0, 182, 255], [0, 185, 255], [0, 188, 255], [0, 190, 255], [0, 192, 255], [0, 195, 255], [0, 198, 255], [0, 200, 255], [0, 202, 255], [0, 205, 255], [0, 208, 255], [0, 210, 255], [0, 212, 255], [0, 215, 255], [0, 218, 255], [0, 220, 255], [0, 222, 255], [0, 225, 255], [0, 228, 255], [0, 230, 255], [0, 232, 255], [0, 235, 255], [0, 238, 255], [0, 240, 255], [0, 242, 255], [0, 245, 255], [0, 248, 255], [0, 250, 255], [2, 252, 255], [5, 253, 255], [8, 254, 255], [11, 255, 255], [15, 255, 255], [20, 255, 255], [25, 255, 255], [30, 255, 255], [35, 255, 255], [40, 255, 255], [45, 255, 255], [50, 255, 255], [55, 255, 255], [60, 255, 255], [65, 255, 255], [70, 255, 255], [75, 255, 255], [80, 255, 255], [85, 255, 255], [90, 255, 255], [95, 255, 255], [100, 255, 255], [105, 255, 255], [110, 255, 255], [115, 255, 255], [120, 255, 255], [125, 255, 255], [130, 255, 255], [135, 255, 255], [140, 255, 255], [145, 255, 255], [150, 255, 255], [155, 255, 255], [160, 255, 255], [165, 255, 255], [170, 255, 255], [175, 255, 255], [180, 255, 255], [185, 255, 255], [190, 255, 255], [195, 255, 255], [200, 255, 255], [205, 255, 255], [210, 255, 255], [215, 255, 255], [220, 255, 255], [225, 255, 255], [230, 255, 255], [235, 255, 255], [240, 255, 255], [245, 255, 255], [250, 255, 255], [255, 255, 255]], "HSV": [[0, 0, 255], [0, 6, 255], [0, 12, 255], [0, 18, 255], [0, 24, 255], [0, 30, 255], [0, 36, 255], [0, 42, 255], [0, 48, 255], [0, 54, 255], [0, 60, 255], [0, 66, 255], [0, 72, 255], [0, 78, 255], [0, 84, 255], [0, 90, 255], [0, 96, 255], [0, 102, 255], [0, 108, 255], [0, 114, 255], [0, 120, 255], [0, 126, 255], [0, 132, 255], [0, 138, 255], [0, 144, 255], [0, 150, 255], [0, 156, 255], [0, 162, 255], [0, 168, 255], [0, 174, 255], [0, 180, 255], [0, 186, 255], [0, 192, 255], [0, 198, 255], [0, 204, 255], [0, 210, 255], [0, 216, 255], [0, 222, 255], [0, 228, 255], [0, 234, 255], [0, 240, 255], [0, 244, 253], [0, 247, 250], [0, 250, 247], [0, 253, 244], [0, 255, 240], [0, 255, 234], [0, 255, 228], [0, 255, 222], [0, 255, 216], [0, 255, 210], [0, 255, 204], [0, 255, 198], [0, 255, 192], [0, 255, 186], [0, 255, 180], [0, 255, 174], [0, 255, 168], [0, 255, 162], [0, 255, 156], [0, 255, 150], [0, 255, 144], [0, 255, 138], [0, 255, 132], [0, 255, 126], [0, 255, 120], [0, 255, 114], [0, 255, 108], [0, 255, 102], [0, 255, 96], [0, 255, 90], [0, 255, 84], [0, 255, 78], [0, 255, 72], [0, 255, 66], [0, 255, 60], [0, 255, 54], [0, 255, 48], [0, 255, 42], [0, 255, 36], [0, 255, 30], [0, 255, 24], [0, 255, 18], [0, 255, 12], [0, 255, 6], [0, 255, 0], [6, 255, 0], [12, 255, 0], [18, 255, 0], [24, 255, 0], [30, 255, 0], [36, 255, 0], [42, 255, 0], [48, 255, 0], [54, 255, 0], [60, 255, 0], [66, 255, 0], [72, 255, 0], [78, 255, 0], [84, 255, 0], [90, 255, 0], [96, 255, 0], [102, 255, 0], [108, 255, 0], [114, 255, 0], [120, 255, 0], [126, 255, 0], [132, 255, 0], [138, 255, 0], [144, 255, 0], [150, 255, 0], [156, 255, 0], [162, 255, 0], [168, 255, 0], [174, 255, 0], [180, 255, 0], [186, 255, 0], [192, 255, 0], [198, 255, 0], [204, 255, 0], [210, 255, 0], [216, 255, 0], [222, 255, 0], [228, 255, 0], [234, 255, 0], [240, 255, 0], [244, 253, 0], [247, 250, 0], [250, 247, 0], [253, 244, 0], [255, 240, 0], [255, 234, 0], [255, 228, 0], [255, 222, 0], [255, 216, 0], [255, 210, 0], [255, 204, 0], [255, 198, 0], [255, 192, 0], [255, 186, 0], [255, 180, 0], [255, 174, 0], [255, 168, 0], [255, 162, 0], [255, 156, 0], [255, 150, 0], [255, 144, 0], [255, 138, 0], [255, 132, 0], [255, 126, 0], [255, 120, 0], [255, 114, 0], [255, 108, 0], [255, 102, 0], [255, 96, 0], [255, 90, 0], [255, 84, 0], [255, 78, 0], [255, 72, 0], [255, 66, 0], [255, 60, 0], [255, 54, 0], [255, 48, 0], [255, 42, 0], [255, 36, 0], [255, 30, 0], [255, 24, 0], [255, 18, 0], [255, 12, 0], [255, 6, 0], [255, 0, 0], [255, 0, 6], [255, 0, 12], [255, 0, 18], [255, 0, 24], [255, 0, 30], [255, 0, 36], [255, 0, 42], [255, 0, 48], [255, 0, 54], [255, 0, 60], [255, 0, 66], [255, 0, 72], [255, 0, 78], [255, 0, 84], [255, 0, 90], [255, 0, 96], [255, 0, 102], [255, 0, 108], [255, 0, 114], [255, 0, 120], [255, 0, 126], [255, 0, 132], [255, 0, 138], [255, 0, 144], [255, 0, 150], [255, 0, 156], [255, 0, 162], [255, 0, 168], [255, 0, 174], [255, 0, 180], [255, 0, 186], [255, 0, 192], [255, 0, 198], [255, 0, 204], [255, 0, 210], [255, 0, 216], [255, 0, 222], [255, 0, 228], [255, 0, 234], [255, 0, 240], [253, 0, 244], [250, 0, 247], [247, 0, 250], [244, 0, 253], [240, 0, 255], [234, 0, 255], [228, 0, 255], [222, 0, 255], [216, 0, 255], [210, 0, 255], [204, 0, 255], [198, 0, 255], [192, 0, 255], [186, 0, 255], [180, 0, 255], [174, 0, 255], [168, 0, 255], [162, 0, 255], [156, 0, 255], [150, 0, 255], [144, 0, 255], [138, 0, 255], [132, 0, 255], [126, 0, 255], [120, 0, 255], [114, 0, 255], [108, 0, 255], [102, 0, 255], [96, 0, 255], [90, 0, 255], [84, 0, 255], [78, 0, 255], [72, 0, 255], [66, 0, 255], [60, 0, 255], [54, 0, 255], [48, 0, 255], [42, 0, 255], [36, 0, 255], [30, 0, 255], [24, 0, 255], [18, 0, 255], [12, 0, 255], [6, 0, 255], [0, 0, 255]], "INFERNO": [[4, 0, 0], [5, 0, 1], [6, 1, 1], [8, 1, 1], [10, 1, 2], [12, 2, 2], [14, 2, 2], [16, 2, 3], [18, 3, 4], [20, 3, 4], [23, 4, 5], [25, 4, 6], [27, 5, 7], [29, 5, 8], [31, 6, 9], [34, 7, 10], [36, 7, 11], [38, 8, 12], [41, 8, 13], [43, 9, 14], [45, 9, 16], [48, 10, 17], [50, 10, 18], [52, 11, 20], [55, 11, 21], [57, 11, 22], [60, 12, 24], [62, 12, 25], [65, 12, 27], [67, 12, 28], [69, 12, 30], [72, 12, 31], [74, 12, 33], [76, 12, 35], [79, 12, 36], [81, 12, 38], [83, 11, 40], [85, 11, 41], [87, 11, 43], [89, 11, 45], [91, 10, 47], [92, 10, 49], [94, 10, 50], [95, 10, 52], [97, 9, 54], [98, 9, 56], [99, 9, 57], [100, 9, 59], [101, 9, 61], [102, 9, 62], [103, 10, 64], [104, 10, 66], [104, 10, 68], [105, 10, 69], [106, 11, 71], [106, 11, 73], [107, 12, 74], [107, 12, 76], [108, 13, 77], [108, 13, 79], [108, 14, 81], [109, 14, 82], [109, 15, 84], [109, 15, 85], [110, 16, 87], [110, 16, 89], [110, 17, 90], [110, 18, 92], [110, 18, 93], [110, 19, 95], [110, 19, 97], [110, 20, 98], [110, 21, 100], [110, 21, 101], [110, 22, 103], [110, 22, 105], [110, 23, 106], [110, 24, 108], [110, 24, 109], [110, 25, 111], [110, 25, 113], [110, 26, 114], [110, 26, 116], [110, 27, 117], [109, 28, 119], [109, 28, 120], [109, 29, 122], [109, 29, 124], [109, 30, 125], [108, 30, 127], [108, 31, 128], [108, 32, 130], [107, 32, 132], [107, 33, 133], [107, 33, 135], [106, 34, 136], [106, 34, 138], [105, 35, 140], [105, 35, 141], [105, 36, 143], [104, 37, 144], [104, 37, 146], [103, 38, 147], [103, 38, 149], [102, 39, 151], [102, 39, 152], [101, 40, 154], [100, 41, 155], [100, 41, 157], [99, 42, 159], [99, 42, 160], [98, 43, 162], [97, 44, 163], [96, 44, 165], [96, 45, 166], [95, 46, 168], [94, 46, 169], [94, 47, 171], [93, 48, 173], [92, 48, 174], [91, 49, 176], [90, 50, 177], [90, 50, 179], [89, 51, 180], [88, 52, 182], [87, 53, 183], [86, 53, 185], [85, 54, 186], [84, 55, 188], [83, 56, 189], [82, 57, 191], [81, 58, 192], [80, 58, 193], [79, 59, 195], [78, 60, 196], [77, 61, 198], [76, 62, 199], [75, 63, 200], [74, 64, 202], [73, 65, 203], [72, 66, 204], [71, 67, 206], [70, 68, 207], [69, 69, 208], [68, 70, 210], [67, 71, 211], [66, 72, 212], [65, 74, 213], [63, 75, 215], [62, 76, 216], [61, 77, 217], [60, 78, 218], [59, 80, 219], [58, 81, 221], [56, 82, 222], [55, 83, 223], [54, 85, 224], [53, 86, 225], [52, 87, 226], [51, 89, 227], [49, 90, 228], [48, 92, 229], [47, 93, 230], [46, 94, 231], [45, 96, 232], [43, 97, 233], [42, 99, 234], [41, 100, 235], [40, 102, 235], [38, 103, 236], [37, 105, 237], [36, 106, 238], [35, 108, 239], [33, 110, 239], [32, 111, 240], [31, 113, 241], [29, 115, 241], [28, 116, 242], [27, 118, 243], [25, 120, 243], [24, 121, 244], [23, 123, 245], [21, 125, 245], [20, 126, 246], [19, 128, 246], [18, 130, 247], [16, 132, 247], [15, 133, 248], [14, 135, 248], [12, 137, 248], [11, 139, 249], [10, 140, 249], [9, 142, 249], [8, 144, 250], [7, 146, 250], [7, 148, 250], [6, 150, 251], [6, 151, 251], [6, 153, 251], [6, 155, 251], [7, 157, 251], [7, 159, 252], [8, 161, 252], [9, 163, 252], [10, 165, 252], [12, 166, 252], [13, 168, 252], [15, 170, 252], [17, 172, 252], [18, 174, 252], [20, 176, 252], [22, 178, 252], [24, 180, 252], [26, 182, 251], [29, 184, 251], [31, 186, 251], [33, 188, 251], [35, 190, 251], [38, 192, 250], [40, 194, 250], [42, 196, 250], [45, 198, 250], [47, 199, 249], [50, 201, 249], [53, 203, 249], [55, 205, 248], [58, 207, 248], [61, 209, 247], [64, 211, 247], [67, 213, 246], [70, 215, 246], [73, 217, 245], [76, 219, 245], [79, 221, 244], [83, 223, 244], [86, 225, 244], [90, 227, 243], [93, 229, 243], [97, 230, 242], [101, 232, 242], [105, 234, 242], [109, 236, 241], [113, 237, 241], [117, 239, 241], [121, 241, 241], [125, 242, 242], [130, 244, 242], [134, 245, 243], [138, 246, 243], [142, 248, 244], [146, 249, 245], [150, 250, 246], [154, 251, 248], [157, 252, 249], [161, 253, 250], [164, 255, 252]], "JET": [[128, 0, 0], [132, 0, 0], [136, 0, 0], [140, 0, 0], [144, 0, 0], [148, 0, 0], [152, 0, 0], [156, 0, 0], [160, 0, 0], [164, 0, 0], [168, 0, 0], [172, 0, 0], [176, 0, 0], [180, 0, 0], [184, 0, 0], [188, 0, 0], [192, 0, 0], [196, 0, 0], [200, 0, 0], [204, 0, 0], [208, 0, 0], [212, 0, 0], [216, 0, 0], [220, 0, 0], [224, 0, 0], [228, 0, 0], [232, 0, 0], [236, 0, 0], [240, 0, 0], [244, 0, 0], [248, 0, 0], [252, 0, 0], [255, 0, 0], [255, 4, 0], [255, 8, 0], [255, 12, 0], [255, 16, 0], [255, 20, 0], [255, 24, 0], [255, 28, 0], [255, 32, 0], [255, 36, 0], [255, 40, 0], [255, 44, 0], [255, 48, 0], [255, 52, 0], [255, 56, 0], [255, 60, 0], [255, 64, 0], [255, 68, 0], [255, 72, 0], [255, 76, 0], [255, 80, 0], [255, 84, 0], [255, 88, 0], [255, 92, 0], [255, 96, 0], [255, 100, 0], [255, 104, 0], [255, 108, 0], [255, 112, 0], [255, 116, 0], [255, 120, 0], [255, 124, 0], [255, 128, 0], [255, 132, 0], [255, 136, 0], [255, 140, 0], [255, 144, 0], [255, 148, 0], [255, 152, 0], [255, 156, 0], [255, 160, 0], [255, 164, 0], [255, 168, 0], [255, 172, 0], [255, 176, 0], [255, 180, 0], [255, 184, 0], [255, 188, 0], [255, 192, 0], [255, 196, 0], [255, 200, 0], [255, 204, 0], [255, 208, 0], [255, 212, 0], [255, 216, 0], [255, 220, 0], [255, 224, 0], [255, 228, 0], [255, 232, 0], [255, 236, 0], [255, 240, 0], [255, 244, 0], [255, 248, 0], [255, 252, 0], [254, 255, 2], [250, 255, 6], [246, 255, 10], [242, 255, 14], [238, 255, 18], [234, 255, 22], [230, 255, 26], [226, 255, 30], [222, 255, 34], [218, 255, 38], [214, 255, 42], [210, 255, 46], [206, 255, 50], [202, 255, 54], [198, 255, 58], [194, 255, 62], [190, 255, 66], [186, 255, 70], [182, 255, 74], [178, 255, 78], [174, 255, 82], [170, 255, 86], [166, 255, 90], [162, 255, 94], [158, 255, 98], [154, 255, 102], [150, 255, 106], [146, 255, 110], [142, 255, 114], [138, 255, 118], [134, 255, 122], [130, 255, 126], [126, 255, 130], [122, 255, 134], [118, 255, 138], [114, 255, 142], [110, 255, 146], [106, 255, 150], [102, 255, 154], [98, 255, 158], [94, 255, 162], [90, 255, 166], [86, 255, 170], [82, 255, 174], [78, 255, 178], [74, 255, 182], [70, 255, 186], [66, 255, 190], [62, 255, 194], [58, 255, 198], [54, 255, 202], [50, 255, 206], [46, 255, 210], [42, 255, 214], [38, 255, 218], [34, 255, 222], [30, 255, 226], [26, 255, 230], [22, 255, 234], [18, 255, 238], [14, 255, 242], [10, 255, 246], [6, 255, 250], [1, 255, 254], [0, 252, 255], [0, 248, 255], [0, 244, 255], [0, 240, 255], [0, 236, 255], [0, 232, 255], [0, 228, 255], [0, 224, 255], [0, 220, 255], [0, 216, 255], [0, 212, 255], [0, 208, 255], [0, 204, 255], [0, 200, 255], [0, 196, 255], [0, 192, 255], [0, 188, 255], [0, 184, 255], [0, 180, 255], [0, 176, 255], [0, 172, 255], [0, 168, 255], [0, 164, 255], [0, 160, 255], [0, 156, 255], [0, 152, 255], [0, 148, 255], [0, 144, 255], [0, 140, 255], [0, 136, 255], [0, 132, 255], [0, 128, 255], [0, 124, 255], [0, 120, 255], [0, 116, 255], [0, 112, 255], [0, 108, 255], [0, 104, 255], [0, 100, 255], [0, 96, 255], [0, 92, 255], [0, 88, 255], [0, 84, 255], [0, 80, 255], [0, 76, 255], [0, 72, 255], [0, 68, 255], [0, 64, 255], [0, 60, 255], [0, 56, 255], [0, 52, 255], [0, 48, 255], [0, 44, 255], [0, 40, 255], [0, 36, 255], [0, 32, 255], [0, 28, 255], [0, 24, 255], [0, 20, 255], [0, 16, 255], [0, 12, 255], [0, 8, 255], [0, 4, 255], [0, 0, 255], [0, 0, 252], [0, 0, 248], [0, 0, 244], [0, 0, 240], [0, 0, 236], [0, 0, 232], [0, 0, 228], [0, 0, 224], [0, 0, 220], [0, 0, 216], [0, 0, 212], [0, 0, 208], [0, 0, 204], [0, 0, 200], [0, 0, 196], [0, 0, 192], [0, 0, 188], [0, 0, 184], [0, 0, 180], [0, 0, 176], [0, 0, 172], [0, 0, 168], [0, 0, 164], [0, 0, 160], [0, 0, 156], [0, 0, 152], [0, 0, 148], [0, 0, 144], [0, 0, 140], [0, 0, 136], [0, 0, 132], [0, 0, 128]], "MAGMA": [[4, 0, 0], [5, 0, 1], [6, 1, 1], [8, 1, 1], [9, 1, 2], [11, 2, 2], [13, 2, 2], [15, 3, 3], [18, 3, 3], [20, 4, 4], [22, 4, 5], [24, 5, 6], [26, 5, 6], [28, 6, 7], [30, 7, 8], [32, 7, 9], [34, 8, 10], [36, 9, 11], [38, 9, 12], [41, 10, 13], [43, 11, 14], [45, 11, 16], [47, 12, 17], [49, 13, 18], [52, 13, 19], [54, 14, 20], [56, 14, 21], [59, 15, 22], [61, 15, 24], [63, 16, 25], [66, 16, 26], [68, 16, 28], [71, 17, 29], [73, 17, 30], [75, 17, 32], [78, 17, 33], [80, 17, 34], [83, 18, 36], [85, 18, 37], [88, 18, 39], [90, 17, 41], [92, 17, 42], [95, 17, 44], [97, 17, 45], [99, 17, 47], [101, 17, 49], [103, 16, 51], [105, 16, 52], [107, 16, 54], [108, 16, 56], [110, 15, 57], [112, 15, 59], [113, 15, 61], [114, 15, 63], [116, 15, 64], [117, 15, 66], [118, 15, 68], [119, 16, 69], [120, 16, 71], [120, 16, 73], [121, 16, 74], [122, 17, 76], [123, 17, 78], [123, 18, 79], [124, 18, 81], [124, 19, 82], [125, 19, 84], [125, 20, 86], [126, 21, 87], [126, 21, 89], [126, 22, 90], [127, 22, 92], [127, 23, 93], [127, 24, 95], [128, 24, 96], [128, 25, 98], [128, 26, 100], [128, 26, 101], [128, 27, 103], [129, 28, 104], [129, 28, 106], [129, 29, 107], [129, 29, 109], [129, 30, 110], [129, 31, 112], [129, 31, 114], [129, 32, 115], [129, 33, 117], [129, 33, 118], [129, 34, 120], [130, 34, 121], [130, 35, 123], [130, 35, 124], [130, 36, 126], [130, 37, 128], [129, 37, 129], [129, 38, 131], [129, 38, 132], [129, 39, 134], [129, 39, 136], [129, 40, 137], [129, 41, 139], [129, 41, 140], [129, 42, 142], [129, 42, 144], [129, 43, 145], [128, 43, 147], [128, 44, 148], [128, 44, 150], [128, 45, 152], [128, 45, 153], [127, 46, 155], [127, 46, 156], [127, 47, 158], [127, 47, 160], [126, 48, 161], [126, 48, 163], [126, 49, 165], [125, 49, 166], [125, 50, 168], [125, 51, 170], [124, 51, 171], [124, 52, 173], [123, 52, 174], [123, 53, 176], [123, 53, 178], [122, 54, 179], [122, 54, 181], [121, 55, 183], [121, 55, 184], [120, 56, 186], [120, 57, 188], [119, 57, 189], [119, 58, 191], [118, 58, 192], [117, 59, 194], [117, 60, 196], [116, 60, 197], [115, 61, 199], [115, 62, 200], [114, 62, 202], [113, 63, 204], [113, 64, 205], [112, 64, 207], [111, 65, 208], [111, 66, 210], [110, 67, 211], [109, 68, 213], [108, 69, 214], [108, 69, 216], [107, 70, 217], [106, 71, 219], [105, 72, 220], [104, 73, 222], [104, 74, 223], [103, 76, 224], [102, 77, 226], [101, 78, 227], [100, 79, 228], [100, 80, 229], [99, 82, 231], [98, 83, 232], [98, 84, 233], [97, 86, 234], [96, 87, 235], [96, 88, 236], [95, 90, 237], [94, 91, 238], [94, 93, 239], [94, 95, 240], [93, 96, 241], [93, 98, 242], [92, 100, 242], [92, 101, 243], [92, 103, 244], [92, 105, 244], [92, 107, 245], [92, 108, 246], [92, 110, 246], [92, 112, 247], [92, 114, 247], [92, 116, 248], [92, 118, 248], [93, 120, 249], [93, 121, 249], [93, 123, 249], [94, 125, 250], [94, 127, 250], [95, 129, 250], [95, 131, 251], [96, 133, 251], [97, 135, 251], [97, 137, 252], [98, 138, 252], [99, 140, 252], [100, 142, 252], [101, 144, 252], [102, 146, 253], [103, 148, 253], [104, 150, 253], [105, 152, 253], [106, 154, 253], [107, 155, 253], [108, 157, 254], [109, 159, 254], [110, 161, 254], [111, 163, 254], [113, 165, 254], [114, 167, 254], [115, 169, 254], [116, 170, 254], [118, 172, 254], [119, 174, 254], [120, 176, 254], [122, 178, 254], [123, 180, 254], [124, 182, 254], [126, 183, 254], [127, 185, 254], [129, 187, 254], [130, 189, 254], [132, 191, 254], [133, 193, 254], [135, 194, 254], [136, 196, 254], [138, 198, 254], [140, 200, 254], [141, 202, 254], [143, 204, 254], [144, 205, 254], [146, 207, 254], [148, 209, 254], [149, 211, 254], [151, 213, 254], [153, 215, 254], [154, 216, 254], [156, 218, 253], [158, 220, 253], [160, 222, 253], [161, 224, 253], [163, 226, 253], [165, 227, 253], [167, 229, 253], [169, 231, 253], [170, 233, 253], [172, 235, 253], [174, 236, 252], [176, 238, 252], [178, 240, 252], [180, 242, 252], [182, 244, 252], [184, 246, 252], [185, 247, 252], [187, 249, 252], [189, 251, 252], [191, 253, 252]], "OCEAN": [[0, 0, 0], [1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0], [5, 0, 0], [6, 0, 0], [7, 0, 0], [8, 0, 0], [9, 0, 0], [10, 0, 0], [11, 0, 0], [12, 0, 0], [13, 0, 0], [14, 0, 0], [15, 0, 0], [16, 0, 0], [17, 0, 0], [18, 0, 0], [19, 0, 0], [20, 0, 0], [21, 0, 0], [22, 0, 0], [23, 0, 0], [24, 0, 0], [25, 0, 0], [26, 0, 0], [27, 0, 0], [28, 0, 0], [29, 0, 0], [30, 0, 0], [31, 0, 0], [32, 0, 0], [33, 0, 0], [34, 0, 0], [35, 0, 0], [36, 0, 0], [37, 0, 0], [38, 0, 0], [39, 0, 0], [40, 0, 0], [41, 0, 0], [42, 0, 0], [43, 0, 0], [44, 0, 0], [45, 0, 0], [46, 0, 0], [47, 0, 0], [48, 0, 0], [49, 0, 0], [50, 0, 0], [51, 0, 0], [52, 0, 0], [53, 0, 0], [54, 0, 0], [55, 0, 0], [56, 0, 0], [57, 0, 0], [58, 0, 0], [59, 0, 0], [60, 0, 0], [61, 0, 0], [62, 0, 0], [63, 0, 0], [64, 0, 0], [65, 0, 0], [66, 0, 0], [67, 0, 0], [68, 0, 0], [69, 0, 0], [70, 0, 0], [71, 0, 0], [72, 0, 0], [73, 0, 0], [74, 0, 0], [75, 0, 0], [76, 0, 0], [77, 0, 0], [78, 0, 0], [79, 0, 0], [80, 0, 0], [81, 0, 0], [82, 0, 0], [83, 0, 0], [84, 0, 0], [85, 0, 0], [86, 2, 0], [87, 3, 0], [88, 4, 0], [89, 6, 0], [90, 8, 0], [91, 9, 0], [92, 10, 0], [93, 12, 0], [94, 13, 0], [95, 15, 0], [96, 17, 0], [97, 18, 0], [98, 20, 0], [99, 21, 0], [100, 22, 0], [101, 24, 0], [102, 26, 0], [103, 27, 0], [104, 28, 0], [105, 30, 0], [106, 32, 0], [107, 33, 0], [108, 34, 0], [109, 36, 0], [110, 38, 0], [111, 39, 0], [112, 40, 0], [113, 42, 0], [114, 44, 0], [115, 45, 0], [116, 46, 0], [117, 48, 0], [118, 50, 0], [119, 51, 0], [120, 52, 0], [121, 54, 0], [122, 56, 0], [123, 57, 0], [124, 58, 0], [125, 60, 0], [126, 62, 0], [127, 63, 0], [128, 64, 0], [129, 66, 0], [130, 68, 0], [131, 69, 0], [132, 70, 0], [133, 72, 0], [134, 74, 0], [135, 75, 0], [136, 76, 0], [137, 78, 0], [138, 80, 0], [139, 81, 0], [140, 82, 0], [141, 84, 0], [142, 86, 0], [143, 87, 0], [144, 88, 0], [145, 90, 0], [146, 92, 0], [147, 93, 0], [148, 94, 0], [149, 96, 0], [150, 98, 0], [151, 99, 0], [152, 100, 0], [153, 102, 0], [154, 104, 0], [155, 105, 0], [156, 106, 0], [157, 108, 0], [158, 110, 0], [159, 111, 0], [160, 112, 0], [161, 114, 0], [162, 116, 0], [163, 117, 0], [164, 118, 0], [165, 120, 0], [166, 122, 0], [167, 123, 0], [168, 124, 0], [169, 126, 0], [170, 128, 0], [171, 129, 3], [172, 130, 6], [173, 132, 9], [174, 134, 12], [175, 135, 15], [176, 136, 18], [177, 138, 21], [178, 140, 24], [179, 141, 27], [180, 142, 30], [181, 144, 33], [182, 146, 36], [183, 147, 39], [184, 148, 42], [185, 150, 45], [186, 152, 48], [187, 153, 51], [188, 154, 54], [189, 156, 57], [190, 158, 60], [191, 159, 63], [192, 161, 66], [193, 162, 69], [194, 164, 72], [195, 165, 75], [196, 166, 78], [197, 168, 81], [198, 170, 84], [199, 171, 87], [200, 172, 90], [201, 174, 93], [202, 176, 96], [203, 177, 99], [204, 178, 102], [205, 180, 105], [206, 182, 108], [207, 183, 111], [208, 184, 114], [209, 186, 117], [210, 188, 120], [211, 189, 123], [212, 190, 126], [213, 192, 129], [214, 194, 132], [215, 195, 135], [216, 196, 138], [217, 198, 141], [218, 200, 144], [219, 201, 147], [220, 202, 150], [221, 204, 153], [222, 206, 156], [223, 207, 159], [224, 208, 162], [225, 210, 165], [226, 212, 168], [227, 213, 171], [228, 214, 174], [229, 216, 177], [230, 218, 180], [231, 219, 183], [232, 220, 186], [233, 222, 189], [234, 224, 192], [235, 225, 195], [236, 226, 198], [237, 228, 201], [238, 230, 204], [239, 231, 207], [240, 232, 210], [241, 234, 213], [242, 236, 216], [243, 237, 219], [244, 238, 222], [245, 240, 225], [246, 242, 228], [247, 243, 231], [248, 244, 234], [249, 246, 237], [250, 248, 240], [251, 249, 243], [252, 251, 246], [253, 252, 249], [254, 254, 252], [255, 255, 255]], "PARULA": [[135, 42, 53], [138, 44, 51], [141, 46, 50], [143, 47, 48], [146, 49, 47], [149, 51, 45], [152, 53, 44], [155, 55, 42], [158, 56, 40], [160, 58, 39], [163, 60, 37], [166, 62, 36], [169, 63, 34], [172, 65, 33], [175, 67, 31], [177, 69, 29], [180, 71, 28], [183, 72, 26], [186, 74, 25], [189, 76, 23], [191, 78, 22], [194, 80, 20], [197, 81, 18], [200, 83, 17], [203, 85, 15], [206, 87, 14], [208, 88, 12], [211, 90, 11], [214, 92, 9], [217, 94, 8], [220, 96, 6], [223, 97, 4], [225, 99, 3], [225, 100, 4], [224, 101, 4], [224, 102, 5], [223, 103, 5], [223, 104, 6], [223, 106, 6], [222, 107, 7], [222, 108, 7], [221, 109, 8], [221, 110, 8], [220, 111, 9], [220, 112, 9], [220, 113, 10], [219, 114, 11], [219, 115, 11], [218, 116, 12], [218, 117, 12], [218, 118, 13], [217, 119, 13], [217, 120, 14], [216, 122, 14], [216, 123, 15], [216, 124, 15], [215, 125, 16], [215, 126, 16], [214, 127, 17], [214, 128, 17], [214, 129, 18], [213, 130, 19], [213, 131, 19], [212, 132, 20], [212, 133, 20], [211, 134, 19], [211, 135, 19], [211, 136, 19], [210, 138, 18], [210, 139, 18], [209, 140, 17], [209, 141, 17], [208, 142, 16], [208, 143, 16], [208, 144, 15], [207, 145, 15], [207, 146, 15], [206, 147, 14], [206, 148, 14], [205, 149, 13], [205, 150, 13], [204, 151, 12], [204, 152, 12], [204, 154, 12], [203, 155, 11], [203, 156, 11], [202, 157, 10], [202, 158, 10], [201, 159, 9], [201, 160, 9], [200, 161, 8], [200, 162, 8], [200, 163, 8], [199, 164, 7], [199, 165, 7], [198, 166, 6], [198, 167, 7], [196, 168, 8], [195, 168, 10], [194, 169, 11], [193, 169, 13], [191, 170, 14], [190, 171, 16], [189, 171, 18], [187, 172, 19], [186, 172, 21], [185, 173, 22], [184, 173, 24], [182, 174, 25], [181, 175, 27], [180, 175, 29], [179, 176, 30], [177, 176, 32], [176, 177, 33], [175, 177, 35], [174, 178, 36], [172, 179, 38], [171, 179, 40], [170, 180, 41], [169, 180, 43], [167, 181, 44], [166, 181, 46], [165, 182, 47], [164, 182, 49], [162, 183, 51], [161, 184, 52], [160, 184, 54], [159, 185, 55], [157, 185, 57], [156, 185, 60], [155, 185, 63], [153, 186, 66], [152, 186, 69], [151, 186, 72], [149, 186, 74], [148, 186, 77], [147, 187, 80], [145, 187, 83], [144, 187, 86], [142, 187, 88], [141, 187, 91], [140, 188, 94], [138, 188, 97], [137, 188, 100], [136, 188, 103], [134, 188, 105], [133, 188, 108], [132, 189, 111], [130, 189, 114], [129, 189, 117], [128, 189, 120], [126, 189, 122], [125, 190, 125], [124, 190, 128], [122, 190, 131], [121, 190, 134], [120, 190, 136], [118, 191, 139], [117, 191, 142], [116, 191, 145], [114, 191, 147], [114, 191, 150], [113, 191, 152], [112, 190, 154], [111, 190, 156], [110, 190, 159], [109, 190, 161], [108, 190, 163], [107, 190, 165], [106, 189, 167], [105, 189, 170], [104, 189, 172], [104, 189, 174], [103, 189, 176], [102, 189, 179], [101, 189, 181], [100, 188, 183], [99, 188, 185], [98, 188, 187], [97, 188, 190], [96, 188, 192], [95, 188, 194], [94, 187, 196], [94, 187, 199], [93, 187, 201], [92, 187, 203], [91, 187, 205], [90, 187, 208], [89, 187, 210], [88, 186, 212], [87, 186, 214], [86, 186, 216], [85, 186, 218], [84, 187, 219], [83, 188, 220], [81, 188, 221], [80, 189, 222], [79, 190, 223], [78, 190, 224], [76, 191, 226], [75, 191, 227], [74, 192, 228], [73, 193, 229], [71, 193, 230], [70, 194, 231], [69, 195, 232], [67, 195, 233], [66, 196, 234], [65, 197, 235], [64, 197, 236], [62, 198, 238], [61, 198, 239], [60, 199, 240], [59, 200, 241], [57, 200, 242], [56, 201, 243], [55, 202, 244], [54, 202, 245], [52, 203, 246], [51, 203, 247], [50, 204, 249], [49, 205, 250], [47, 205, 251], [46, 206, 252], [45, 207, 252], [44, 209, 252], [43, 210, 252], [42, 211, 252], [41, 213, 252], [40, 214, 251], [39, 216, 251], [38, 217, 251], [37, 219, 251], [36, 220, 251], [35, 221, 251], [34, 223, 251], [33, 224, 251], [32, 226, 251], [31, 227, 251], [30, 228, 251], [29, 230, 250], [28, 231, 250], [27, 233, 250], [26, 234, 250], [25, 235, 250], [24, 237, 250], [23, 238, 250], [22, 240, 250], [21, 241, 250], [20, 243, 250], [19, 244, 249], [18, 245, 249], [17, 247, 249], [16, 248, 249], [15, 250, 249], [14, 251, 249]], "PINK": [[0, 0, 0], [6, 6, 10], [13, 13, 20], [19, 19, 30], [26, 26, 40], [29, 29, 44], [31, 31, 48], [34, 34, 52], [37, 37, 56], [39, 39, 60], [41, 41, 63], [43, 43, 66], [45, 45, 69], [47, 47, 72], [49, 49, 74], [50, 50, 77], [52, 52, 80], [54, 54, 82], [55, 55, 84], [57, 57, 87], [58, 58, 89], [60, 60, 91], [61, 61, 93], [62, 62, 95], [64, 64, 98], [65, 65, 100], [66, 66, 101], [68, 68, 103], [69, 69, 105], [70, 70, 107], [71, 71, 109], [73, 73, 111], [74, 74, 113], [75, 75, 114], [76, 76, 116], [77, 77, 118], [78, 78, 119], [79, 79, 121], [80, 80, 123], [81, 81, 124], [82, 82, 126], [83, 83, 128], [84, 84, 129], [85, 85, 131], [86, 86, 132], [87, 87, 134], [88, 88, 135], [89, 89, 137], [90, 90, 138], [91, 91, 139], [92, 92, 141], [93, 93, 142], [94, 94, 144], [95, 95, 145], [96, 96, 146], [97, 97, 148], [98, 98, 149], [98, 98, 150], [99, 99, 152], [100, 100, 153], [101, 101, 154], [102, 102, 156], [103, 103, 157], [103, 103, 158], [104, 104, 159], [105, 105, 161], [106, 106, 162], [107, 107, 163], [108, 108, 164], [108, 108, 165], [109, 109, 167], [110, 110, 168], [111, 111, 169], [111, 111, 170], [112, 112, 171], [113, 113, 172], [114, 114, 174], [114, 114, 175], [115, 115, 176], [116, 116, 177], [117, 117, 178], [117, 117, 179], [118, 118, 180], [119, 119, 181], [119, 119, 183], [120, 120, 184], [121, 121, 185], [122, 122, 186], [122, 122, 187], [123, 123, 188], [124, 124, 189], [124, 124, 190], [125, 125, 191], [126, 126, 192], [126, 127, 193], [127, 128, 194], [128, 129, 195], [128, 130, 195], [129, 131, 196], [130, 133, 196], [130, 134, 197], [131, 136, 197], [132, 137, 198], [132, 138, 198], [133, 140, 198], [134, 141, 199], [134, 143, 199], [135, 144, 200], [135, 145, 200], [136, 147, 201], [137, 148, 201], [137, 149, 201], [138, 151, 202], [139, 152, 202], [139, 153, 203], [140, 155, 203], [140, 156, 203], [141, 157, 204], [142, 159, 204], [142, 160, 205], [143, 161, 205], [143, 162, 206], [144, 163, 206], [145, 165, 206], [145, 166, 207], [146, 167, 207], [146, 168, 208], [147, 169, 208], [148, 171, 208], [148, 172, 209], [149, 173, 209], [149, 174, 210], [150, 175, 210], [150, 176, 210], [151, 177, 211], [151, 179, 211], [152, 180, 212], [153, 181, 212], [153, 182, 212], [154, 183, 213], [154, 184, 213], [155, 185, 214], [155, 186, 214], [156, 187, 214], [156, 188, 215], [157, 189, 215], [158, 190, 216], [158, 191, 216], [159, 192, 216], [159, 193, 217], [160, 194, 217], [160, 195, 218], [161, 197, 218], [161, 198, 218], [162, 199, 219], [162, 200, 219], [163, 201, 220], [163, 201, 220], [164, 202, 220], [164, 203, 221], [165, 204, 221], [165, 205, 221], [166, 206, 222], [166, 207, 222], [167, 208, 223], [167, 209, 223], [168, 210, 223], [168, 211, 224], [169, 212, 224], [169, 213, 225], [170, 214, 225], [170, 215, 225], [171, 216, 226], [171, 217, 226], [172, 218, 226], [172, 218, 227], [173, 219, 227], [173, 220, 228], [174, 221, 228], [174, 222, 228], [175, 223, 229], [175, 224, 229], [176, 225, 229], [176, 226, 230], [177, 227, 230], [177, 227, 230], [178, 228, 231], [178, 229, 231], [179, 230, 232], [179, 231, 232], [180, 232, 232], [181, 232, 233], [182, 233, 233], [183, 233, 233], [184, 234, 234], [186, 234, 234], [187, 235, 235], [188, 235, 235], [190, 235, 235], [191, 236, 236], [192, 236, 236], [194, 236, 236], [195, 237, 237], [196, 237, 237], [198, 237, 237], [199, 238, 238], [200, 238, 238], [201, 238, 238], [203, 239, 239], [204, 239, 239], [205, 240, 240], [206, 240, 240], [208, 240, 240], [209, 241, 241], [210, 241, 241], [211, 241, 241], [212, 242, 242], [214, 242, 242], [215, 242, 242], [216, 243, 243], [217, 243, 243], [218, 243, 243], [220, 244, 244], [221, 244, 244], [222, 244, 244], [223, 245, 245], [224, 245, 245], [225, 245, 245], [226, 246, 246], [228, 246, 246], [229, 247, 247], [230, 247, 247], [231, 247, 247], [232, 248, 248], [233, 248, 248], [234, 248, 248], [235, 249, 249], [236, 249, 249], [237, 249, 249], [238, 250, 250], [240, 250, 250], [241, 250, 250], [242, 251, 251], [243, 251, 251], [244, 251, 251], [245, 252, 252], [246, 252, 252], [247, 252, 252], [248, 253, 253], [249, 253, 253], [250, 253, 253], [251, 254, 254], [252, 254, 254], [253, 254, 254], [254, 255, 255], [255, 255, 255]], "PLASMA": [[135, 8, 13], [136, 7, 16], [137, 7, 19], [138, 7, 22], [140, 6, 25], [141, 6, 27], [142, 6, 29], [143, 6, 32], [144, 6, 34], [145, 6, 36], [145, 5, 38], [146, 5, 40], [147, 5, 42], [148, 5, 44], [149, 5, 46], [150, 5, 47], [151, 5, 49], [151, 5, 51], [152, 4, 53], [153, 4, 55], [154, 4, 56], [154, 4, 58], [155, 4, 60], [156, 4, 62], [156, 4, 63], [157, 4, 65], [158, 3, 67], [158, 3, 68], [159, 3, 70], [159, 3, 72], [160, 3, 73], [161, 3, 75], [161, 2, 76], [162, 2, 78], [162, 2, 80], [163, 2, 81], [163, 2, 83], [164, 2, 85], [164, 1, 86], [164, 1, 88], [165, 1, 89], [165, 1, 91], [166, 1, 92], [166, 1, 94], [166, 1, 96], [167, 0, 97], [167, 0, 99], [167, 0, 100], [167, 0, 102], [168, 0, 103], [168, 0, 105], [168, 0, 106], [168, 0, 108], [168, 0, 110], [168, 0, 111], [168, 0, 113], [168, 1, 114], [168, 1, 116], [168, 1, 117], [168, 1, 119], [168, 1, 120], [168, 2, 122], [168, 2, 123], [168, 3, 125], [168, 3, 126], [168, 4, 128], [167, 4, 129], [167, 5, 131], [167, 5, 132], [166, 6, 134], [166, 7, 135], [166, 8, 136], [165, 9, 138], [165, 10, 139], [165, 11, 141], [164, 12, 142], [164, 13, 143], [163, 14, 145], [163, 15, 146], [162, 16, 148], [161, 17, 149], [161, 19, 150], [160, 20, 152], [159, 21, 153], [159, 22, 154], [158, 23, 156], [157, 24, 157], [157, 25, 158], [156, 26, 160], [155, 27, 161], [154, 29, 162], [154, 30, 163], [153, 31, 165], [152, 32, 166], [151, 33, 167], [150, 34, 168], [149, 35, 170], [148, 36, 171], [148, 38, 172], [147, 39, 173], [146, 40, 174], [145, 41, 176], [144, 42, 177], [143, 43, 178], [142, 44, 179], [141, 46, 180], [140, 47, 181], [139, 48, 182], [138, 49, 183], [137, 50, 184], [136, 51, 186], [136, 52, 187], [135, 53, 188], [134, 55, 189], [133, 56, 190], [132, 57, 191], [131, 58, 192], [130, 59, 193], [129, 60, 194], [128, 61, 195], [127, 62, 196], [126, 64, 197], [125, 65, 198], [124, 66, 199], [123, 67, 200], [122, 68, 201], [122, 69, 202], [121, 70, 203], [120, 71, 204], [119, 73, 204], [118, 74, 205], [117, 75, 206], [116, 76, 207], [115, 77, 208], [114, 78, 209], [113, 79, 210], [113, 81, 211], [112, 82, 212], [111, 83, 213], [110, 84, 213], [109, 85, 214], [108, 86, 215], [107, 87, 216], [106, 88, 217], [106, 90, 218], [105, 91, 218], [104, 92, 219], [103, 93, 220], [102, 94, 221], [101, 95, 222], [100, 97, 222], [99, 98, 223], [99, 99, 224], [98, 100, 225], [97, 101, 226], [96, 102, 226], [95, 104, 227], [94, 105, 228], [93, 106, 229], [93, 107, 229], [92, 108, 230], [91, 110, 231], [90, 111, 231], [89, 112, 232], [88, 113, 233], [87, 114, 233], [87, 116, 234], [86, 117, 235], [85, 118, 235], [84, 119, 236], [83, 121, 237], [82, 122, 237], [81, 123, 238], [81, 124, 239], [80, 126, 239], [79, 127, 240], [78, 128, 240], [77, 129, 241], [76, 131, 241], [75, 132, 242], [75, 133, 243], [74, 135, 243], [73, 136, 244], [72, 137, 244], [71, 139, 245], [70, 140, 245], [69, 141, 246], [68, 143, 246], [68, 144, 247], [67, 145, 247], [66, 147, 247], [65, 148, 248], [64, 149, 248], [63, 151, 249], [62, 152, 249], [62, 154, 249], [61, 155, 250], [60, 156, 250], [59, 158, 250], [58, 159, 251], [57, 161, 251], [56, 162, 251], [56, 163, 252], [55, 165, 252], [54, 166, 252], [53, 168, 252], [52, 169, 252], [51, 171, 253], [51, 172, 253], [50, 174, 253], [49, 175, 253], [48, 177, 253], [47, 178, 253], [47, 180, 253], [46, 181, 253], [45, 183, 254], [44, 184, 254], [44, 186, 254], [43, 187, 254], [42, 189, 254], [42, 190, 254], [41, 192, 254], [41, 194, 253], [40, 195, 253], [39, 197, 253], [39, 198, 253], [39, 200, 253], [38, 202, 253], [38, 203, 253], [37, 205, 252], [37, 206, 252], [37, 208, 252], [37, 210, 252], [36, 211, 251], [36, 213, 251], [36, 215, 251], [36, 216, 250], [36, 218, 250], [36, 220, 249], [37, 221, 249], [37, 223, 248], [37, 225, 248], [37, 226, 247], [37, 228, 247], [38, 230, 246], [38, 232, 246], [38, 233, 245], [39, 235, 245], [39, 237, 244], [39, 238, 243], [39, 240, 243], [39, 242, 242], [38, 244, 241], [37, 245, 241], [36, 247, 240], [33, 249, 240]], "RAINBOW": [[0, 0, 255], [0, 2, 255], [0, 5, 255], [0, 8, 255], [0, 10, 255], [0, 12, 255], [0, 15, 255], [0, 18, 255], [0, 20, 255], [0, 22, 255], [0, 25, 255], [0, 27, 255], [0, 30, 255], [0, 32, 255], [0, 35, 255], [0, 38, 255], [0, 40, 255], [0, 42, 255], [0, 45, 255], [0, 48, 255], [0, 50, 255], [0, 52, 255], [0, 55, 255], [0, 57, 255], [0, 60, 255], [0, 62, 255], [0, 65, 255], [0, 68, 255], [0, 70, 255], [0, 72, 255], [0, 75, 255], [0, 78, 255], [0, 80, 255], [0, 82, 255], [0, 85, 255], [0, 88, 255], [0, 90, 255], [0, 92, 255], [0, 95, 255], [0, 98, 255], [0, 100, 255], [0, 102, 255], [0, 105, 255], [0, 108, 255], [0, 110, 255], [0, 112, 255], [0, 115, 255], [0, 117, 255], [0, 120, 255], [0, 122, 255], [0, 125, 255], [0, 128, 255], [0, 130, 255], [0, 132, 255], [0, 135, 255], [0, 138, 255], [0, 140, 255], [0, 142, 255], [0, 145, 255], [0, 148, 255], [0, 150, 255], [0, 152, 255], [0, 155, 255], [0, 158, 255], [0, 160, 255], [0, 162, 255], [0, 165, 255], [0, 168, 255], [0, 170, 255], [0, 172, 255], [0, 175, 255], [0, 178, 255], [0, 180, 255], [0, 182, 255], [0, 185, 255], [0, 188, 255], [0, 190, 255], [0, 192, 255], [0, 195, 255], [0, 198, 255], [0, 200, 255], [0, 202, 255], [0, 205, 255], [0, 208, 255], [0, 210, 255], [0, 212, 255], [0, 215, 255], [0, 218, 255], [0, 220, 255], [0, 223, 255], [0, 225, 255], [0, 228, 255], [0, 230, 255], [0, 232, 255], [0, 235, 255], [0, 238, 255], [0, 240, 255], [0, 243, 255], [0, 245, 255], [0, 248, 255], [0, 250, 255], [0, 252, 255], [0, 253, 252], [0, 254, 248], [0, 254, 244], [0, 255, 240], [0, 255, 235], [0, 255, 230], [0, 255, 225], [0, 255, 220], [0, 255, 215], [0, 255, 210], [0, 255, 205], [0, 255, 200], [0, 255, 195], [0, 255, 190], [0, 255, 185], [0, 255, 180], [0, 255, 175], [0, 255, 170], [0, 255, 165], [0, 255, 160], [0, 255, 155], [0, 255, 150], [0, 255, 145], [0, 255, 140], [0, 255, 135], [0, 255, 130], [0, 255, 125], [0, 255, 120], [0, 255, 115], [0, 255, 110], [0, 255, 105], [0, 255, 100], [0, 255, 95], [0, 255, 90], [0, 255, 85], [0, 255, 80], [0, 255, 75], [0, 255, 70], [0, 255, 65], [0, 255, 60], [0, 255, 55], [0, 255, 50], [0, 255, 45], [0, 255, 40], [0, 255, 35], [0, 255, 30], [0, 255, 25], [0, 255, 20], [0, 255, 15], [1, 254, 11], [2, 253, 7], [3, 252, 3], [5, 250, 0], [10, 245, 0], [15, 240, 0], [20, 235, 0], [25, 230, 0], [30, 225, 0], [35, 220, 0], [40, 215, 0], [45, 210, 0], [50, 205, 0], [55, 200, 0], [60, 195, 0], [65, 190, 0], [70, 185, 0], [75, 180, 0], [80, 175, 0], [85, 170, 0], [90, 165, 0], [95, 160, 0], [100, 155, 0], [105, 150, 0], [110, 145, 0], [115, 140, 0], [120, 135, 0], [125, 130, 0], [130, 125, 0], [135, 120, 0], [140, 115, 0], [145, 110, 0], [150, 105, 0], [155, 100, 0], [160, 95, 0], [165, 90, 0], [170, 85, 0], [175, 80, 0], [180, 75, 0], [185, 70, 0], [190, 65, 0], [195, 60, 0], [200, 55, 0], [205, 50, 0], [210, 45, 0], [215, 40, 0], [220, 35, 0], [225, 30, 0], [230, 25, 0], [235, 20, 0], [240, 15, 0], [245, 10, 0], [248, 7, 1], [250, 5, 3], [252, 3, 5], [254, 1, 7], [255, 0, 10], [255, 0, 13], [255, 0, 17], [255, 0, 20], [255, 0, 23], [255, 0, 27], [255, 0, 30], [255, 0, 33], [255, 0, 37], [255, 0, 40], [255, 0, 43], [255, 0, 47], [255, 0, 50], [255, 0, 53], [255, 0, 57], [255, 0, 60], [255, 0, 63], [255, 0, 67], [255, 0, 70], [255, 0, 73], [255, 0, 77], [255, 0, 80], [255, 0, 83], [255, 0, 87], [255, 0, 90], [255, 0, 93], [255, 0, 97], [255, 0, 100], [255, 0, 103], [255, 0, 107], [255, 0, 110], [255, 0, 113], [255, 0, 117], [255, 0, 120], [255, 0, 123], [255, 0, 127], [255, 0, 130], [255, 0, 133], [255, 0, 137], [255, 0, 140], [255, 0, 143], [255, 0, 147], [255, 0, 150], [255, 0, 153], [255, 0, 157], [255, 0, 160], [255, 0, 163], [255, 0, 167], [255, 0, 170]], "SPRING": [[255, 0, 255], [254, 1, 255], [253, 2, 255], [252, 3, 255], [251, 4, 255], [250, 5, 255], [249, 6, 255], [248, 7, 255], [247, 8, 255], [246, 9, 255], [245, 10, 255], [244, 11, 255], [243, 12, 255], [242, 13, 255], [241, 14, 255], [240, 15, 255], [239, 16, 255], [238, 17, 255], [237, 18, 255], [236, 19, 255], [235, 20, 255], [234, 21, 255], [233, 22, 255], [232, 23, 255], [231, 24, 255], [230, 25, 255], [229, 26, 255], [228, 27, 255], [227, 28, 255], [226, 29, 255], [225, 30, 255], [224, 31, 255], [223, 32, 255], [222, 33, 255], [221, 34, 255], [220, 35, 255], [219, 36, 255], [218, 37, 255], [217, 38, 255], [216, 39, 255], [215, 40, 255], [214, 41, 255], [213, 42, 255], [212, 43, 255], [211, 44, 255], [210, 45, 255], [209, 46, 255], [208, 47, 255], [207, 48, 255], [206, 49, 255], [205, 50, 255], [204, 51, 255], [203, 52, 255], [202, 53, 255], [201, 54, 255], [200, 55, 255], [199, 56, 255], [198, 57, 255], [197, 58, 255], [196, 59, 255], [195, 60, 255], [194, 61, 255], [193, 62, 255], [192, 63, 255], [191, 64, 255], [190, 65, 255], [189, 66, 255], [188, 67, 255], [187, 68, 255], [186, 69, 255], [185, 70, 255], [184, 71, 255], [183, 72, 255], [182, 73, 255], [181, 74, 255], [180, 75, 255], [179, 76, 255], [178, 77, 255], [177, 78, 255], [176, 79, 255], [175, 80, 255], [174, 81, 255], [173, 82, 255], [172, 83, 255], [171, 84, 255], [170, 85, 255], [169, 86, 255], [168, 87, 255], [167, 88, 255], [166, 89, 255], [165, 90, 255], [164, 91, 255], [163, 92, 255], [162, 93, 255], [161, 94, 255], [160, 95, 255], [159, 96, 255], [158, 97, 255], [157, 98, 255], [156, 99, 255], [155, 100, 255], [154, 101, 255], [153, 102, 255], [152, 103, 255], [151, 104, 255], [150, 105, 255], [149, 106, 255], [148, 107, 255], [147, 108, 255], [146, 109, 255], [145, 110, 255], [144, 111, 255], [143, 112, 255], [142, 113, 255], [141, 114, 255], [140, 115, 255], [139, 116, 255], [138, 117, 255], [137, 118, 255], [136, 119, 255], [135, 120, 255], [134, 121, 255], [133, 122, 255], [132, 123, 255], [131, 124, 255], [130, 125, 255], [129, 126, 255], [128, 127, 255], [127, 128, 255], [126, 129, 255], [125, 130, 255], [124, 131, 255], [123, 132, 255], [122, 133, 255], [121, 134, 255], [120, 135, 255], [119, 136, 255], [118, 137, 255], [117, 138, 255], [116, 139, 255], [115, 140, 255], [114, 141, 255], [113, 142, 255], [112, 143, 255], [111, 144, 255], [110, 145, 255], [109, 146, 255], [108, 147, 255], [107, 148, 255], [106, 149, 255], [105, 150, 255], [104, 151, 255], [103, 152, 255], [102, 153, 255], [101, 154, 255], [100, 155, 255], [99, 156, 255], [98, 157, 255], [97, 158, 255], [96, 159, 255], [95, 160, 255], [94, 161, 255], [93, 162, 255], [92, 163, 255], [91, 164, 255], [90, 165, 255], [89, 166, 255], [88, 167, 255], [87, 168, 255], [86, 169, 255], [85, 170, 255], [84, 171, 255], [83, 172, 255], [82, 173, 255], [81, 174, 255], [80, 175, 255], [79, 176, 255], [78, 177, 255], [77, 178, 255], [76, 179, 255], [75, 180, 255], [74, 181, 255], [73, 182, 255], [72, 183, 255], [71, 184, 255], [70, 185, 255], [69, 186, 255], [68, 187, 255], [67, 188, 255], [66, 189, 255], [65, 190, 255], [64, 191, 255], [63, 192, 255], [62, 193, 255], [61, 194, 255], [60, 195, 255], [59, 196, 255], [58, 197, 255], [57, 198, 255], [56, 199, 255], [55, 200, 255], [54, 201, 255], [53, 202, 255], [52, 203, 255], [51, 204, 255], [50, 205, 255], [49, 206, 255], [48, 207, 255], [47, 208, 255], [46, 209, 255], [45, 210, 255], [44, 211, 255], [43, 212, 255], [42, 213, 255], [41, 214, 255], [40, 215, 255], [39, 216, 255], [38, 217, 255], [37, 218, 255], [36, 219, 255], [35, 220, 255], [34, 221, 255], [33, 222, 255], [32, 223, 255], [31, 224, 255], [30, 225, 255], [29, 226, 255], [28, 227, 255], [27, 228, 255], [26, 229, 255], [25, 230, 255], [24, 231, 255], [23, 232, 255], [22, 233, 255], [21, 234, 255], [20, 235, 255], [19, 236, 255], [18, 237, 255], [17, 238, 255], [16, 239, 255], [15, 240, 255], [14, 241, 255], [13, 242, 255], [12, 243, 255], [11, 244, 255], [10, 245, 255], [9, 246, 255], [8, 247, 255], [7, 248, 255], [6, 249, 255], [5, 250, 255], [4, 251, 255], [3, 252, 255], [2, 253, 255], [1, 254, 255], [0, 255, 255]], "SUMMER": [[102, 128, 0], [102, 128, 1], [102, 128, 2], [102, 129, 3], [102, 130, 4], [102, 130, 5], [102, 130, 6], [102, 131, 7], [102, 132, 8], [102, 132, 9], [102, 132, 10], [102, 133, 11], [102, 134, 12], [102, 134, 13], [102, 134, 14], [102, 135, 15], [102, 136, 16], [102, 136, 17], [102, 136, 18], [102, 137, 19], [102, 138, 20], [102, 138, 21], [102, 138, 22], [102, 139, 23], [102, 140, 24], [102, 140, 25], [102, 140, 26], [102, 141, 27], [102, 142, 28], [102, 142, 29], [102, 142, 30], [102, 143, 31], [102, 144, 32], [102, 144, 33], [102, 144, 34], [102, 145, 35], [102, 146, 36], [102, 146, 37], [102, 146, 38], [102, 147, 39], [102, 148, 40], [102, 148, 41], [102, 148, 42], [102, 149, 43], [102, 150, 44], [102, 150, 45], [102, 150, 46], [102, 151, 47], [102, 152, 48], [102, 152, 49], [102, 152, 50], [102, 153, 51], [102, 154, 52], [102, 154, 53], [102, 154, 54], [102, 155, 55], [102, 156, 56], [102, 156, 57], [102, 156, 58], [102, 157, 59], [102, 158, 60], [102, 158, 61], [102, 158, 62], [102, 159, 63], [102, 160, 64], [102, 160, 65], [102, 160, 66], [102, 161, 67], [102, 162, 68], [102, 162, 69], [102, 162, 70], [102, 163, 71], [102, 164, 72], [102, 164, 73], [102, 164, 74], [102, 165, 75], [102, 166, 76], [102, 166, 77], [102, 166, 78], [102, 167, 79], [102, 168, 80], [102, 168, 81], [102, 168, 82], [102, 169, 83], [102, 170, 84], [102, 170, 85], [102, 170, 86], [102, 171, 87], [102, 172, 88], [102, 172, 89], [102, 172, 90], [102, 173, 91], [102, 174, 92], [102, 174, 93], [102, 174, 94], [102, 175, 95], [102, 176, 96], [102, 176, 97], [102, 176, 98], [102, 177, 99], [102, 178, 100], [102, 178, 101], [102, 178, 102], [102, 179, 103], [102, 180, 104], [102, 180, 105], [102, 180, 106], [102, 181, 107], [102, 182, 108], [102, 182, 109], [102, 182, 110], [102, 183, 111], [102, 184, 112], [102, 184, 113], [102, 184, 114], [102, 185, 115], [102, 186, 116], [102, 186, 117], [102, 186, 118], [102, 187, 119], [102, 188, 120], [102, 188, 121], [102, 188, 122], [102, 189, 123], [102, 190, 124], [102, 190, 125], [102, 190, 126], [102, 191, 127], [102, 192, 128], [102, 192, 129], [102, 192, 130], [102, 193, 131], [102, 194, 132], [102, 194, 133], [102, 194, 134], [102, 195, 135], [102, 196, 136], [102, 196, 137], [102, 196, 138], [102, 197, 139], [102, 198, 140], [102, 198, 141], [102, 198, 142], [102, 199, 143], [102, 200, 144], [102, 200, 145], [102, 200, 146], [102, 201, 147], [102, 202, 148], [102, 202, 149], [102, 202, 150], [102, 203, 151], [102, 204, 152], [102, 204, 153], [102, 204, 154], [102, 205, 155], [102, 206, 156], [102, 206, 157], [102, 206, 158], [102, 207, 159], [102, 208, 160], [102, 208, 161], [102, 208, 162], [102, 209, 163], [102, 210, 164], [102, 210, 165], [102, 210, 166], [102, 211, 167], [102, 212, 168], [102, 212, 169], [102, 212, 170], [102, 213, 171], [102, 214, 172], [102, 214, 173], [102, 214, 174], [102, 215, 175], [102, 216, 176], [102, 216, 177], [102, 216, 178], [102, 217, 179], [102, 218, 180], [102, 218, 181], [102, 218, 182], [102, 219, 183], [102, 220, 184], [102, 220, 185], [102, 220, 186], [102, 221, 187], [102, 222, 188], [102, 222, 189], [102, 222, 190], [102, 223, 191], [102, 224, 192], [102, 224, 193], [102, 224, 194], [102, 225, 195], [102, 226, 196], [102, 226, 197], [102, 226, 198], [102, 227, 199], [102, 228, 200], [102, 228, 201], [102, 228, 202], [102, 229, 203], [102, 230, 204], [102, 230, 205], [102, 230, 206], [102, 231, 207], [102, 232, 208], [102, 232, 209], [102, 232, 210], [102, 233, 211], [102, 234, 212], [102, 234, 213], [102, 234, 214], [102, 235, 215], [102, 236, 216], [102, 236, 217], [102, 236, 218], [102, 237, 219], [102, 238, 220], [102, 238, 221], [102, 238, 222], [102, 239, 223], [102, 240, 224], [102, 240, 225], [102, 240, 226], [102, 241, 227], [102, 242, 228], [102, 242, 229], [102, 242, 230], [102, 243, 231], [102, 244, 232], [102, 244, 233], [102, 244, 234], [102, 245, 235], [102, 246, 236], [102, 246, 237], [102, 246, 238], [102, 247, 239], [102, 248, 240], [102, 248, 241], [102, 248, 242], [102, 249, 243], [102, 250, 244], [102, 250, 245], [102, 250, 246], [102, 251, 247], [102, 252, 248], [102, 252, 249], [102, 252, 250], [102, 253, 251], [102, 254, 252], [102, 254, 253], [102, 254, 254], [102, 255, 255]], "TURBO": [[59, 18, 48], [67, 21, 50], [74, 24, 51], [81, 27, 52], [88, 30, 53], [95, 33, 54], [102, 36, 55], [109, 39, 56], [115, 42, 57], [121, 45, 58], [128, 47, 59], [134, 50, 60], [139, 53, 61], [145, 56, 62], [151, 59, 63], [156, 62, 63], [162, 64, 64], [167, 67, 65], [172, 70, 65], [177, 73, 66], [181, 75, 66], [186, 78, 67], [191, 81, 68], [195, 84, 68], [199, 86, 68], [203, 89, 69], [207, 92, 69], [211, 94, 69], [214, 97, 70], [218, 100, 70], [221, 102, 70], [224, 105, 70], [227, 107, 70], [230, 110, 71], [233, 113, 71], [235, 115, 71], [238, 118, 71], [240, 120, 71], [242, 123, 71], [244, 125, 70], [246, 128, 70], [248, 130, 70], [250, 133, 70], [251, 135, 70], [252, 138, 69], [253, 140, 69], [254, 143, 68], [254, 145, 67], [255, 148, 66], [255, 150, 65], [255, 153, 64], [254, 155, 62], [254, 158, 61], [253, 160, 59], [252, 163, 58], [251, 165, 56], [250, 168, 55], [248, 171, 53], [247, 173, 51], [245, 175, 49], [244, 178, 47], [242, 180, 46], [240, 183, 44], [238, 185, 42], [235, 188, 40], [233, 190, 39], [231, 192, 37], [228, 195, 35], [226, 197, 34], [223, 199, 32], [221, 201, 31], [218, 203, 30], [216, 205, 28], [213, 208, 27], [210, 210, 26], [208, 212, 26], [205, 213, 25], [202, 215, 24], [200, 217, 24], [197, 219, 24], [194, 221, 24], [192, 222, 24], [189, 224, 24], [187, 226, 25], [185, 227, 25], [182, 228, 26], [180, 230, 28], [178, 231, 29], [175, 233, 31], [172, 234, 32], [170, 235, 34], [167, 236, 37], [164, 238, 39], [161, 239, 42], [158, 240, 44], [155, 241, 47], [152, 242, 50], [148, 243, 53], [145, 244, 56], [142, 245, 60], [138, 246, 63], [135, 247, 67], [132, 248, 70], [128, 248, 74], [125, 249, 78], [122, 250, 82], [118, 250, 85], [115, 251, 89], [111, 252, 93], [108, 252, 97], [105, 253, 101], [102, 253, 105], [98, 254, 109], [95, 254, 113], [92, 254, 117], [89, 254, 121], [86, 255, 125], [83, 255, 128], [81, 255, 132], [78, 255, 136], [75, 255, 139], [73, 255, 143], [71, 255, 146], [68, 254, 150], [66, 254, 153], [64, 254, 156], [63, 253, 159], [61, 253, 161], [60, 252, 164], [58, 252, 167], [57, 251, 169], [56, 251, 172], [55, 250, 175], [54, 249, 177], [54, 248, 180], [53, 247, 183], [53, 246, 185], [52, 245, 188], [52, 244, 190], [52, 243, 193], [52, 241, 195], [52, 240, 198], [52, 239, 200], [52, 237, 203], [52, 236, 205], [52, 234, 208], [53, 233, 210], [53, 231, 212], [53, 229, 215], [54, 228, 217], [54, 226, 219], [55, 224, 221], [55, 223, 223], [55, 221, 225], [56, 219, 227], [56, 217, 229], [57, 215, 231], [57, 213, 233], [57, 211, 235], [58, 209, 236], [58, 207, 238], [58, 205, 239], [58, 203, 241], [58, 201, 242], [58, 199, 244], [58, 197, 245], [58, 195, 246], [58, 193, 247], [57, 190, 248], [57, 188, 249], [57, 186, 250], [56, 184, 251], [55, 182, 251], [54, 179, 252], [54, 177, 252], [53, 174, 253], [52, 172, 253], [51, 169, 254], [50, 167, 254], [49, 164, 254], [48, 161, 254], [47, 158, 254], [45, 155, 254], [44, 153, 254], [43, 150, 254], [42, 147, 254], [41, 144, 254], [39, 141, 253], [38, 138, 253], [37, 135, 252], [35, 132, 252], [34, 129, 251], [33, 126, 251], [31, 123, 250], [30, 120, 249], [29, 117, 249], [28, 114, 248], [26, 111, 247], [25, 108, 246], [24, 105, 245], [23, 102, 244], [21, 99, 243], [20, 96, 242], [19, 93, 241], [18, 91, 240], [17, 88, 239], [16, 85, 237], [15, 83, 236], [14, 80, 235], [13, 78, 234], [12, 75, 232], [12, 73, 231], [11, 71, 229], [10, 69, 228], [10, 67, 226], [9, 65, 225], [8, 63, 223], [8, 61, 221], [7, 59, 220], [7, 57, 218], [6, 55, 216], [6, 53, 214], [5, 51, 212], [5, 49, 210], [5, 47, 208], [4, 45, 206], [4, 43, 204], [4, 42, 202], [3, 40, 200], [3, 38, 197], [3, 37, 195], [2, 35, 193], [2, 33, 190], [2, 32, 188], [2, 30, 185], [2, 29, 183], [1, 27, 180], [1, 26, 178], [1, 24, 175], [1, 23, 172], [1, 22, 169], [1, 20, 167], [1, 19, 164], [1, 18, 161], [1, 16, 158], [1, 15, 155], [1, 14, 152], [1, 13, 149], [1, 11, 146], [1, 10, 142], [2, 9, 139], [2, 8, 136], [2, 7, 133], [2, 6, 129], [2, 5, 126], [3, 4, 122]], "TWILIGHT": [[55, 20, 48], [57, 19, 49], [58, 18, 50], [60, 17, 51], [62, 17, 52], [64, 17, 53], [66, 17, 54], [68, 17, 55], [70, 17, 57], [73, 17, 58], [75, 17, 60], [78, 17, 61], [81, 18, 63], [84, 18, 65], [87, 18, 66], [90, 19, 68], [93, 20, 70], [97, 20, 71], [100, 21, 73], [104, 22, 75], [107, 23, 76], [110, 24, 78], [114, 25, 79], [117, 26, 81], [121, 28, 82], [124, 29, 83], [127, 31, 84], [130, 33, 86], [133, 35, 87], [136, 37, 87], [139, 39, 88], [142, 41, 89], [144, 43, 90], [147, 46, 90], [149, 48, 91], [151, 50, 91], [154, 53, 92], [156, 55, 92], [158, 57, 93], [160, 60, 93], [161, 62, 93], [163, 65, 93], [165, 67, 94], [166, 70, 94], [168, 72, 94], [169, 74, 94], [171, 77, 94], [172, 79, 94], [173, 82, 94], [174, 84, 94], [175, 86, 95], [177, 89, 95], [178, 91, 95], [179, 94, 95], [179, 96, 95], [180, 98, 95], [181, 100, 95], [182, 103, 96], [183, 105, 96], [183, 107, 96], [184, 110, 96], [185, 112, 97], [185, 114, 97], [186, 116, 98], [187, 118, 98], [187, 121, 99], [188, 123, 99], [188, 125, 100], [189, 127, 101], [189, 129, 101], [189, 131, 102], [190, 133, 103], [190, 136, 104], [191, 138, 105], [191, 140, 106], [191, 142, 108], [192, 144, 109], [192, 146, 110], [192, 148, 112], [193, 150, 113], [193, 152, 115], [193, 154, 116], [193, 156, 118], [194, 158, 120], [194, 159, 122], [194, 161, 124], [195, 163, 126], [195, 165, 128], [195, 167, 130], [196, 169, 132], [196, 171, 135], [197, 173, 137], [197, 174, 139], [197, 176, 142], [198, 178, 144], [198, 180, 147], [199, 181, 150], [199, 183, 152], [200, 185, 155], [201, 187, 158], [201, 188, 161], [202, 190, 164], [203, 192, 167], [203, 193, 170], [204, 195, 173], [205, 197, 176], [206, 198, 179], [207, 200, 182], [208, 201, 185], [209, 203, 188], [210, 204, 191], [211, 206, 194], [213, 207, 197], [214, 208, 199], [215, 209, 202], [216, 211, 205], [217, 212, 207], [218, 213, 209], [219, 214, 212], [220, 215, 214], [221, 215, 216], [222, 216, 218], [223, 217, 219], [224, 217, 221], [225, 217, 222], [225, 217, 223], [226, 217, 225], [226, 217, 226], [226, 217, 226], [224, 217, 226], [223, 216, 225], [221, 216, 225], [220, 215, 225], [218, 214, 224], [216, 213, 223], [214, 212, 223], [212, 211, 222], [209, 210, 222], [207, 208, 221], [204, 206, 220], [201, 205, 219], [198, 203, 219], [195, 201, 218], [191, 199, 217], [188, 197, 216], [185, 195, 215], [181, 193, 214], [178, 191, 213], [174, 189, 212], [171, 187, 211], [168, 185, 210], [164, 183, 210], [161, 180, 209], [157, 178, 208], [154, 176, 207], [151, 174, 207], [147, 171, 206], [144, 169, 205], [141, 167, 205], [138, 164, 204], [135, 162, 204], [132, 159, 203], [129, 157, 203], [126, 155, 202], [124, 152, 201], [121, 150, 201], [118, 147, 200], [116, 145, 200], [113, 142, 199], [111, 140, 199], [109, 137, 198], [107, 135, 197], [105, 132, 197], [103, 130, 196], [101, 128, 195], [99, 125, 195], [98, 123, 194], [96, 120, 193], [94, 118, 192], [93, 115, 192], [92, 113, 191], [91, 110, 190], [89, 108, 189], [88, 106, 188], [87, 103, 187], [86, 101, 186], [86, 99, 185], [85, 96, 183], [84, 94, 182], [83, 92, 181], [83, 89, 180], [82, 87, 178], [82, 85, 177], [81, 83, 176], [81, 80, 174], [81, 78, 173], [80, 76, 172], [80, 74, 170], [80, 72, 168], [80, 70, 167], [80, 68, 165], [80, 65, 164], [80, 63, 162], [80, 61, 160], [80, 59, 158], [80, 57, 157], [80, 55, 155], [80, 54, 153], [80, 52, 151], [80, 50, 149], [80, 48, 147], [80, 46, 145], [80, 45, 143], [80, 43, 141], [80, 42, 138], [80, 40, 136], [80, 39, 134], [80, 37, 132], [80, 36, 129], [80, 35, 127], [80, 33, 124], [80, 32, 122], [79, 31, 119], [79, 30, 117], [79, 29, 114], [78, 28, 112], [78, 27, 109], [77, 27, 106], [77, 26, 104], [76, 25, 101], [75, 24, 99], [74, 24, 96], [73, 23, 93], [72, 22, 91], [71, 22, 88], [70, 21, 85], [69, 21, 83], [68, 20, 80], [67, 20, 78], [66, 19, 75], [65, 19, 73], [64, 18, 71], [63, 18, 68], [62, 18, 66], [61, 18, 64], [60, 17, 62], [59, 17, 60], [58, 17, 58], [58, 17, 57], [57, 17, 55], [56, 17, 54], [56, 18, 52], [55, 18, 51], [55, 18, 50], [55, 19, 49], [54, 20, 47]], "VIRIDIS": [[84, 1, 68], [86, 2, 68], [87, 4, 69], [89, 5, 69], [90, 7, 70], [92, 8, 70], [93, 10, 70], [94, 11, 70], [96, 13, 71], [97, 14, 71], [99, 16, 71], [100, 17, 71], [101, 19, 71], [103, 20, 72], [104, 22, 72], [105, 23, 72], [106, 24, 72], [108, 26, 72], [109, 27, 72], [110, 28, 72], [111, 29, 72], [112, 31, 72], [113, 32, 72], [115, 33, 72], [116, 35, 72], [117, 36, 72], [118, 37, 72], [119, 38, 72], [120, 40, 72], [121, 41, 72], [122, 42, 71], [122, 44, 71], [123, 45, 71], [124, 46, 71], [125, 47, 71], [126, 48, 70], [126, 50, 70], [127, 51, 70], [128, 52, 70], [129, 53, 69], [129, 55, 69], [130, 56, 69], [131, 57, 68], [131, 58, 68], [132, 59, 68], [132, 61, 67], [133, 62, 67], [133, 63, 66], [134, 64, 66], [134, 65, 66], [135, 66, 65], [135, 68, 65], [136, 69, 64], [136, 70, 64], [136, 71, 63], [137, 72, 63], [137, 73, 62], [137, 74, 62], [138, 76, 62], [138, 77, 61], [138, 78, 61], [138, 79, 60], [139, 80, 60], [139, 81, 59], [139, 82, 59], [139, 83, 58], [140, 84, 58], [140, 85, 57], [140, 86, 57], [140, 88, 56], [140, 89, 56], [140, 90, 55], [141, 91, 55], [141, 92, 54], [141, 93, 54], [141, 94, 53], [141, 95, 53], [141, 96, 52], [141, 97, 52], [141, 98, 51], [141, 99, 51], [142, 100, 50], [142, 101, 50], [142, 102, 49], [142, 103, 49], [142, 104, 49], [142, 105, 48], [142, 106, 48], [142, 107, 47], [142, 108, 47], [142, 109, 46], [142, 110, 46], [142, 111, 46], [142, 112, 45], [142, 113, 45], [142, 113, 44], [142, 114, 44], [142, 115, 44], [142, 116, 43], [142, 117, 43], [142, 118, 42], [142, 119, 42], [142, 120, 42], [142, 121, 41], [142, 122, 41], [142, 123, 41], [142, 124, 40], [142, 125, 40], [142, 126, 39], [142, 127, 39], [142, 128, 39], [142, 129, 38], [142, 130, 38], [142, 130, 38], [142, 131, 37], [142, 132, 37], [142, 133, 37], [142, 134, 36], [142, 135, 36], [142, 136, 35], [142, 137, 35], [141, 138, 35], [141, 139, 34], [141, 140, 34], [141, 141, 34], [141, 142, 33], [141, 143, 33], [141, 144, 33], [140, 145, 33], [140, 146, 32], [140, 146, 32], [140, 147, 32], [140, 148, 31], [139, 149, 31], [139, 150, 31], [139, 151, 31], [139, 152, 31], [138, 153, 31], [138, 154, 31], [138, 155, 30], [137, 156, 30], [137, 157, 30], [137, 158, 31], [136, 159, 31], [136, 160, 31], [136, 161, 31], [135, 161, 31], [135, 162, 31], [134, 163, 32], [134, 164, 32], [133, 165, 33], [133, 166, 33], [133, 167, 34], [132, 168, 34], [131, 169, 35], [131, 170, 36], [130, 171, 37], [130, 172, 37], [129, 173, 38], [129, 173, 39], [128, 174, 40], [127, 175, 41], [127, 176, 42], [126, 177, 44], [125, 178, 45], [124, 179, 46], [124, 180, 47], [123, 181, 49], [122, 182, 50], [121, 182, 52], [121, 183, 53], [120, 184, 55], [119, 185, 56], [118, 186, 58], [117, 187, 59], [116, 188, 61], [115, 188, 63], [114, 189, 64], [113, 190, 66], [112, 191, 68], [111, 192, 70], [110, 193, 72], [109, 193, 74], [108, 194, 76], [107, 195, 78], [106, 196, 80], [105, 197, 82], [104, 197, 84], [103, 198, 86], [101, 199, 88], [100, 200, 90], [99, 200, 92], [98, 201, 94], [96, 202, 96], [95, 203, 99], [94, 203, 101], [92, 204, 103], [91, 205, 105], [90, 205, 108], [88, 206, 110], [87, 207, 112], [86, 208, 115], [84, 208, 117], [83, 209, 119], [81, 209, 122], [80, 210, 124], [78, 211, 127], [77, 211, 129], [75, 212, 132], [73, 213, 134], [72, 213, 137], [70, 214, 139], [69, 214, 142], [67, 215, 144], [65, 215, 147], [64, 216, 149], [62, 216, 152], [60, 217, 155], [59, 217, 157], [57, 218, 160], [55, 218, 162], [54, 219, 165], [52, 219, 168], [50, 220, 170], [48, 220, 173], [47, 221, 176], [45, 221, 178], [43, 222, 181], [41, 222, 184], [40, 222, 186], [38, 223, 189], [37, 223, 192], [35, 223, 194], [33, 224, 197], [32, 224, 200], [31, 225, 202], [29, 225, 205], [28, 225, 208], [27, 226, 210], [26, 226, 213], [25, 226, 216], [25, 227, 218], [24, 227, 221], [24, 227, 223], [24, 228, 226], [25, 228, 229], [25, 228, 231], [26, 229, 234], [27, 229, 236], [28, 229, 239], [29, 229, 241], [30, 230, 244], [32, 230, 246], [33, 230, 248], [35, 231, 251], [37, 231, 253]], "WINTER": [[255, 0, 0], [254, 1, 0], [254, 2, 0], [254, 3, 0], [253, 4, 0], [252, 5, 0], [252, 6, 0], [252, 7, 0], [251, 8, 0], [250, 9, 0], [250, 10, 0], [250, 11, 0], [249, 12, 0], [248, 13, 0], [248, 14, 0], [248, 15, 0], [247, 16, 0], [246, 17, 0], [246, 18, 0], [246, 19, 0], [245, 20, 0], [244, 21, 0], [244, 22, 0], [244, 23, 0], [243, 24, 0], [242, 25, 0], [242, 26, 0], [242, 27, 0], [241, 28, 0], [240, 29, 0], [240, 30, 0], [240, 31, 0], [239, 32, 0], [238, 33, 0], [238, 34, 0], [238, 35, 0], [237, 36, 0], [236, 37, 0], [236, 38, 0], [236, 39, 0], [235, 40, 0], [234, 41, 0], [234, 42, 0], [234, 43, 0], [233, 44, 0], [232, 45, 0], [232, 46, 0], [232, 47, 0], [231, 48, 0], [230, 49, 0], [230, 50, 0], [230, 51, 0], [229, 52, 0], [228, 53, 0], [228, 54, 0], [228, 55, 0], [227, 56, 0], [226, 57, 0], [226, 58, 0], [226, 59, 0], [225, 60, 0], [224, 61, 0], [224, 62, 0], [224, 63, 0], [223, 64, 0], [222, 65, 0], [222, 66, 0], [222, 67, 0], [221, 68, 0], [221, 69, 0], [220, 70, 0], [220, 71, 0], [219, 72, 0], [219, 73, 0], [218, 74, 0], [218, 75, 0], [217, 76, 0], [217, 77, 0], [216, 78, 0], [216, 79, 0], [215, 80, 0], [215, 81, 0], [214, 82, 0], [214, 83, 0], [213, 84, 0], [213, 85, 0], [212, 86, 0], [212, 87, 0], [211, 88, 0], [211, 89, 0], [210, 90, 0], [210, 91, 0], [209, 92, 0], [209, 93, 0], [208, 94, 0], [208, 95, 0], [207, 96, 0], [206, 97, 0], [206, 98, 0], [206, 99, 0], [205, 100, 0], [204, 101, 0], [204, 102, 0], [204, 103, 0], [203, 104, 0], [202, 105, 0], [202, 106, 0], [202, 107, 0], [201, 108, 0], [200, 109, 0], [200, 110, 0], [200, 111, 0], [199, 112, 0], [198, 113, 0], [198, 114, 0], [198, 115, 0], [197, 116, 0], [196, 117, 0], [196, 118, 0], [196, 119, 0], [195, 120, 0], [194, 121, 0], [194, 122, 0], [194, 123, 0], [193, 124, 0], [192, 125, 0], [192, 126, 0], [192, 127, 0], [191, 128, 0], [190, 129, 0], [190, 130, 0], [190, 131, 0], [189, 132, 0], [188, 133, 0], [188, 134, 0], [188, 135, 0], [187, 136, 0], [186, 137, 0], [186, 138, 0], [186, 139, 0], [185, 140, 0], [184, 141, 0], [184, 142, 0], [184, 143, 0], [183, 144, 0], [182, 145, 0], [182, 146, 0], [182, 147, 0], [181, 148, 0], [180, 149, 0], [180, 150, 0], [180, 151, 0], [179, 152, 0], [178, 153, 0], [178, 154, 0], [178, 155, 0], [177, 156, 0], [176, 157, 0], [176, 158, 0], [176, 159, 0], [175, 160, 0], [174, 161, 0], [174, 162, 0], [174, 163, 0], [173, 164, 0], [172, 165, 0], [172, 166, 0], [172, 167, 0], [171, 168, 0], [170, 169, 0], [170, 170, 0], [170, 171, 0], [169, 172, 0], [168, 173, 0], [168, 174, 0], [168, 175, 0], [167, 176, 0], [166, 177, 0], [166, 178, 0], [166, 179, 0], [165, 180, 0], [164, 181, 0], [164, 182, 0], [164, 183, 0], [163, 184, 0], [162, 185, 0], [162, 186, 0], [162, 187, 0], [161, 188, 0], [160, 189, 0], [160, 190, 0], [160, 191, 0], [159, 192, 0], [158, 193, 0], [158, 194, 0], [158, 195, 0], [157, 196, 0], [156, 197, 0], [156, 198, 0], [156, 199, 0], [155, 200, 0], [154, 201, 0], [154, 202, 0], [154, 203, 0], [153, 204, 0], [152, 205, 0], [152, 206, 0], [152, 207, 0], [151, 208, 0], [150, 209, 0], [150, 210, 0], [150, 211, 0], [149, 212, 0], [148, 213, 0], [148, 214, 0], [148, 215, 0], [147, 216, 0], [146, 217, 0], [146, 218, 0], [146, 219, 0], [145, 220, 0], [144, 221, 0], [144, 222, 0], [144, 223, 0], [143, 224, 0], [142, 225, 0], [142, 226, 0], [142, 227, 0], [141, 228, 0], [140, 229, 0], [140, 230, 0], [140, 231, 0], [139, 232, 0], [138, 233, 0], [138, 234, 0], [138, 235, 0], [137, 236, 0], [136, 237, 0], [136, 238, 0], [136, 239, 0], [135, 240, 0], [134, 241, 0], [134, 242, 0], [134, 243, 0], [133, 244, 0], [132, 245, 0], [132, 246, 0], [132, 247, 0], [131, 248, 0], [130, 249, 0], [130, 250, 0], [130, 251, 0], [129, 252, 0], [128, 253, 0], [128, 254, 0], [128, 255, 0]]        
    }


    /**
     * applyColorMap方法应用颜色映射到图像
     *
     * @param {string} name 颜色映射的名称,默认为 'JET',可选的颜色映射名称由{@link Img#COLORMAPNAMES}中给定的值确定。
     * @returns {Image} 应用颜色映射后的图像
     */
    applyColorMap(name = 'JET'){
        name=name.toUpperCase();
        let cmaps=Img.COLORMAPS[name];
        if (!cmaps) {
            console.error(`Color map '${name}' not found.`);
            console.warn('Available color maps:', Img.COLORMAPNAMES)
        }
        let outimg = this.empty(false);
        for (let y = 0; y < this.height; y++) {
            for (let x = 0; x < this.width; x++) {
                let [r, g, b, a] = this.getpixel(x, y);
                [r, g, b] = cmaps[parseInt((r + g + b) / 3)];
                outimg.setpixel(x, y, [r, g, b, a]);
            }
        }
        return outimg;
    }


    /**
     * applycolormap,将图像转灰度后,为灰度图像添加warm to cool的通过添加伪彩色warm to cool
     * cool to warm from vtkjs colormaps.json
     * 颜色映射的计算来自于vtkjs colormaps.json中的定义
     * @returns {Img} - 生成的图像对象。
     * @deprecated 该方法已被废弃,请使用applyColorMap代替。
     */
    applycolormap(name='JET') {
        let colormap = new Map();
        function calc(x) {
            x = x / 255;
            let r, g, b;
            let wa, wb;
            if (x < 0.5) {
                wa = (0.5 - x) / 0.5
                wb = x / 0.5
                r = wa * 0.705882352941 + wb * 0.865;
                g = wa * 0.0156862745098 + wb * 0.865;
                b = wa * 149019607843 + wb * 0.865;
            }
            else {
                wa = (1 - x) / 0.5
                wb = (x - 0.5) / 0.5
                r = wa * 0.865 + wb * 0.23137254902;
                g = wa * 0.865 + wb * 0.298039215686;
                b = wa * 0.865 + wb * 0.752941176471;
            }
            return [r * 255, g * 255, b * 255]
        }
        for (let i = 0; i < 256; i++) {
            colormap.set(i, calc(i));
        }
        let outimg = this.empty(false);
        for (let y = 0; y < this.height; y++) {
            for (let x = 0; x < this.width; x++) {
                let [r, g, b, a] = this.getpixel(x, y);
                [r, g, b] = colormap.get(parseInt((r + g + b) / 3));
                outimg.setpixel(x, y, [r, g, b, a]);
            }
        }
        return outimg;
    }

    /**
     * resize,将图像resize到指定大小
     * @param {number} [height = 256] - 输出图像的高度,默认为256。
     * @param {number} [width = 256] - 输出图像的宽度,默认为256。
     * @returns {Img} - 生成的图像对象。
     */
    resize(height = 256, width = 256) {
        if (!this.iscasnew) this.update();
        let canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        let ctx = canvas.getContext('2d');
        //ctx.imageSmoothingEnabled = true;  // Enable interpolation
        ctx.drawImage(this.cas, 0, 0, this.width, this.height, 0, 0, width, height)
        return Img.fromcanvas(canvas)
    }

    /**
     * rotate,将图像旋转deg角度
     * @param {number} [deg = 45] - 旋转角度,默认为45度。
     * @returns {Img} - 生成的图像对象。
     */
    rotate(deg = 45) {
        if (!this.iscasnew) this.update();
        let angleInRadians = deg * Math.PI / 180; // 将角度转换为弧度
        let canvas = document.createElement('canvas');
        canvas.width = this.width;
        canvas.height = this.height;
        let ctx = canvas.getContext('2d');
        ctx.translate(this.cas.width / 2, this.cas.height / 2); // 将原点移动到图像中心
        ctx.rotate(-angleInRadians); // 旋转图像
        ctx.drawImage(this.cas, -this.width / 2, -this.height / 2); // 绘制旋转后的图像
        return Img.fromcanvas(canvas);
    }

    /**
     * opacity,设置图像不透明度,越大越不透明,取值0~255
     * @param {number} [value = 255] - 不透明度的值,默认为255。
     * @returns {Img} - 生成的图像对象。
     */
    opacity(value = 255) {
        //设置图像不透明度,越大越不透明,取值0~255
        //this.fill(value,3); 这种和下面的用哪种实现?
        if (this.iscasnew) this.update();
        this.data.forEach(function (x, idx, arr) { if (idx % 4 == 3) arr[idx] = value });
        return this;
    }

    /**
     * bitplane,计算比特面,cidx取哪个通道,bitnum取哪个比特位,返回值是0、1的数组Imgarray对象
     * @param {number} cidx - 取哪个通道,取值0~3。
     * @param {number} bitnum - 取哪个比特位,取值0~7。
     * @returns {Img} - 生成的图像对象。
     */
    bitplane(cidx, bitnum) {
        //计算比特面,cidx取哪个通道,bitnum取哪个比特位,返回值是0、1的数组Imgarray对象
        //this.dsplit或循环   this.vectorize  
        // 推荐双层循环,对宽和高进行遍历,通过使用getelement获取通道的值
        const bitplaneArray = new ImgArray({ height: this.height, width: this.width, channel: 1 })
        for (let y = 0; y < this.height; y++) {
            for (let x = 0; x < this.width; x++) {
                //计算当前像素在data数组的索引
                //let index = this.idxtoelidx(y, x, cidx);
                //let val = this.data[index];              //获得指定通道的值
                let val = this.getel(y, x, cidx);
                const bitValue = (val >> bitnum) & 1;    //提取指定比特位的值
                //bitplaneArray.data[y * this.width + x] = bitValue * 255;
                bitplaneArray.setel(y, x, 0, bitValue * 255);
            }
        }
        return bitplaneArray;
    }

    /**
     * RGBtoYCbCr,将RGB图像转换为YCbCr图像
     * @returns {Img} - 生成的图像对象。
     */
    RGBtoYCbCr() {
        //将RGB图像转换为YCbCr图像
        // 创建一个与原始图像相同大小的新图像对象
        let ycbcrImg = new Img({ width: this.width, height: this.height, channel: this.channel });
        if (this.channel < 3) {
            console.error("该图像不是RGB图像,无法进行转换")
        }
        for (let i = 0; i < this.data.length; i += this.channel) {
            // 获取当前像素的RGB值
            let r = this.data[i];
            let g = this.data[i + 1];
            let b = this.data[i + 2];
            // 计算YCbCr值
            let y = 0.299 * r + 0.587 * g + 0.114 * b;
            let Cb = (-0.168736 * r - 0.331264 * g + 0.5 * b) + 128;
            let Cr = (0.5 * r - 0.418688 * g - 0.081312 * b) + 128;
            ycbcrImg.data[i] = y;
            ycbcrImg.data[i + 1] = Cb;
            ycbcrImg.data[i + 2] = Cr;
            ycbcrImg.data[i + 3] = this.data[i + 3];
        }
        // console.log(ycbcrImg);
        return ycbcrImg;
    }

    /**
     * YCbCrtoRGB,将YCbCr图像转换为RGB图像
     * @returns {Img} - 生成的图像对象。
     */
    YCbCrtoRGB() {
        let rgbImg = new Img({ width: this.width, height: this.height, channel: this.channel });
        if (this.channel < 3) {
            console.error("该图像无法转换为RGB图像")
        }
        for (let i = 0; i < this.data.length; i += this.channel) {
            // 获取当前像素的RGB值
            let y = this.data[i];
            let Cb = this.data[i + 1];
            let Cr = this.data[i + 2];
            // 计算YCbCr值
            let r = y + 1.402 * (Cr - 128);
            let g = y - 0.344136 * (Cb - 128) - 0.714136 * (Cr - 128);
            let b = y + 1.772 * (Cb - 128);
            rgbImg.data[i] = r;
            rgbImg.data[i + 1] = g;
            rgbImg.data[i + 2] = b;
            rgbImg.data[i + 3] = this.data[i + 3];
        }
        // console.log(rgbImg);
        return rgbImg;
    }

    /**
     * drawbox,在图像上画一个指定大小的矩形
     * @param {number} [x = 50] - 矩形左上角的x坐标,默认为50。
     * @param {number} [y = 50] - 矩形左上角的y坐标,默认为50。
     * @param {number} [w = 50] - 矩形的宽度,默认为50。
     * @param {number} [h = 50] - 矩形的高度,默认为50。
     * @param {string} [color = '#000000'] - 矩形的颜色,默认为'#000000'。
     * @param {number} [linewidth = 2] - 矩形的线宽,默认为2。
    * @param {boolean} [fill = false] - 是否填充矩
    * @param {string} [fillcolor = '#000000'] - 填充的颜色,默认为'#000000'。
    * @returns {Img} - 生成的图像对象。
    */
    drawbox(x = 50, y = 50, w = 50, h = 50, color = '#000000', linewidth = 2, fill = false, fillcolor = '#000000') {
        //在图像上画一个指定大小的矩形
        if (!this.iscasnew) this.update();
        // 设置线宽和颜色
        this.ctx.lineWidth = linewidth;
        this.ctx.strokeStyle = color;

        // 绘制矩形框
        if (fill) {
            this.ctx.fillStyle = fillcolor;
            this.ctx.fillRect(x, y, w, h);
        } else {
            this.ctx.strokeRect(x, y, w, h);
        }
        // 将绘制后的图像数据更新到Img对象中
        //this.data = self.ctx.getImageData(0, 0, this.width, this.height).data;
        this.iscasnew = true;
        return this;
    }

    /**
     * drawtext,在图像上绘制文字
     * @param {number} [x = 50] - 文字的x坐标,默认为50。
     * @param {number} [y = 50] - 文字的y坐标,默认为50。
     * @param {string} [text = 'Hello'] - 要绘制的文字,默认为'Hello'。
     * @param {string} [color = 'red'] - 文字的颜色,默认为'red'。
     * @param {number} [linewidth = 2] - 文字的线宽,默认为2。
    * @param {number} [fontSize = 20] - 文字的字体大小,默认为20。
    * @returns {Img} - 生成的图像对象。
    */
    drawtext({ x = 50, y = 50, text = 'Hello', color = 'red', linewidth = 2, fontSize = 20 } = {}) {
        //在图像上绘制文字
        // 设置文字属性,包括字体大小
        if (!this.iscasnew) this.update();
        this.ctx.font = `${fontSize}px Arial`; // 设置字体大小和样式
        this.ctx.fillStyle = color;
        this.ctx.lineWidth = linewidth;
        // 写入文字
        this.ctx.fillText(text, x, y);
        // 将绘制后的图像数据更新到Img对象中
        //this.data = self.ctx.getImageData(0, 0, this.width, this.height).data;
        this.iscasnew = true;
        return this;
    }

    /**
     * show,向网页中插入图像
     * @param {HTMLCanvasElement} [cas = null] - 要插入的canvas元素,默认为null。
     * @returns {Img} - 生成的图像对象。
    */
    show(cas = null) {
        //向网页中插入图像
        let cs = cas ? cas : document.createElement('canvas');
        cs.width = this.width;
        cs.height = this.height;
        if (this.iscasnew) this.update();
        this.tocanvas(cs);
        cas ? null : document.body.appendChild(cs);
        return this;
    }
}

/**
 * @class DICM,a simple DICOM format parser
 * 一个简单的DICM格式解析器,读取的数组可转为ImgArray数组
 * 参考资料:
 * https://www.cnblogs.com/assassinx/archive/2013/01/09/dicomViewer.html
 * https://www.cnblogs.com/sean-zeng/p/18061402
 * dicom 文件可分为5个部分:
 * 1. 前128个字节为导言区,全都是0;
 * 2. 128-132字节,共4个字节为“DICM”字符串,用于识别检测DICM格式。
 * 3. 文件元数据区, 从132字节开始,使用显示的VR格式记录, 格式有两种形式:
 * 当VR值为OB OW OF UT SQ UN这几种时,格式为:组号(2),元素号(2),vr(2),预留(2),值长度(4),值 ;
 * 当VR值为其他类型时,格式为:组号(2),元素号(2),vr(2),值长度(4),值;
 * 4. 数据元数据区,可以使用显示VR或隐式VR,VR为显式时,数据元数据区为:
 * VR为隐匿时,数据元数据区为:
 * 组号(2),元素号(2),值长度(4),值;
 * 不包含VR项,VR项需要根据组号和元素号确定;
 * 5. 数据区域
 */
class DICM {

    /**
     * 构造函数
     * @param {ArrayBuffer} bf - 二进制数据
     */
    constructor(bf) {
        this.bf = bf;
        this.bytearr = new Uint8Array(bf)
        //识别DICM格式
        this.offset = this.#checkformat(); //the offset should be 132

        //3.解析文件元数据
        [this.islitteendian, this.isexplicitvr] = [true, true];
        this.filemeta = this.#parseheadmeta();
        //4.解析数据元数据
        this.datameta = this.#parsedatameta();
    }

    /**
     * checkformat,检查DICM格式
     * @returns {number} - 检测到DICM格式的偏移量。
     */
    #checkformat() {
        //1. 前128个字节为导言区,全都是0;需要跳过
        //2. 128-132字节,共4个字节为“DICM”字符串,用于识别检测DICM格式。
        let offset = 128;
        let formatstr = String.fromCharCode(...this.bytearr.slice(offset, offset += 4))
        if (formatstr != 'DICM') throw ("it's not dicm image");
        return offset;
    }

    /**
     * getheaderonetag,获取 DICOM 文件的元数据头部信息。
     * 该方法解析文件中的特定位置并提取出元数据区域的相关内容。
     * 
     * 元数据区域包含了多种信息,其中包括:
     * - 组号和元素号(2 字节)
     * - VR(值表示数据类型,2 字节)
     * - 值长度(4 字节或 2 字节,取决于 VR 类型)
     * - 数据值(根据 VR 和长度变化)
     * @param {boolean} [islitteendian=true] - 是否采用小端字节序,默认为 `true`,如果使用大端字节序,则传入 `false`。
     * @returns {Object} 返回一个包含以下属性的对象:
     * - {string} attrcode - 由组号和元素号组成的字符串,格式为 "组号,元素号"。
     * - {string} vr - VR 字段的值,表示数据的类型。
     * - {number} datalength - 数据部分的长度(以字节为单位)。
     * - {Uint8Array} data - 解析出的原始数据值。
     * - {any} value - 根据 VR 解析后的值。
     */
    #getheaderonetag(islitteendian = true) {
        //3. 文件元数据区, 从132字节开始,使用显示的VR格式记录, 格式有两种形式:
        let offset = this.offset
        let arr = this.bytearr;
        //当VR值为OB OW OF UT SQ UN这几种时,格式为:组号(2),元素号(2),vr(2),预留(2),值长度(4),值 ;
        //当VR值为其他类型时,格式为:组号(2),元素号(2),vr(2),值长度(2),值;

        let attrcode = arr.slice(offset, offset += 4);
        attrcode = Array.from(attrcode).map(byte => byte.toString(16).padStart(2, '0'));
        attrcode = attrcode[1] + attrcode[0] + "," + attrcode[3] + attrcode[2];

        let vr = String.fromCharCode(...arr.slice(offset, offset += 2));
        let datalength = 0;
        if (['OB', 'OW', 'OF', 'UT', 'SQ', 'UN'].includes(vr)) {
            offset += 2;
            datalength = new DataView(arr.buffer.slice(offset, offset += 4)).getUint32(0, islitteendian);
        }
        else {
            datalength = new DataView(arr.buffer.slice(offset, offset += 2)).getUint16(0, islitteendian);
        }
        let data = arr.slice(offset, offset += datalength);
        let value = this.vrparse(vr, data, islitteendian);
        this.offset = offset;
        return { attrcode, vr, datalength, data, value }
    }

    /**
     * parseheadmeta
     * @returns {Map} - 文件元数据。
     * 
     */
    #parseheadmeta() {
        //    3. 文件元数据区, 从132字节开始,使用显示的VR格式记录, 格式有两种形式:
        let firsthead = this.#getheaderonetag();
        let headendpos = 0;
        //第一个数据块给出整个文件元数据区的长度,不包含该firsthead的长度
        if (firsthead.attrcode == '0002,0000')   //其值记录了整个文件元数据的长度)
        {
            headendpos = this.offset + firsthead.value;
        }
        else throw new Error('file head error');
        let headmeta = new Map([[firsthead.attrcode, firsthead]]);
        while (this.offset < headendpos) {
            let metadata = this.#getheaderonetag();
            if (metadata.attrcode == '0002,0010') {
                [this.islitteendian, this.isexplicitvr] = this.#getdatametamode(metadata.attrcode, metadata.value);
            }
            headmeta.set(metadata.attrcode, metadata);
        }
        return headmeta;
    }

    /**
     * getdatametamode
     * @param {string} attrcode - 元数据项的属性代码。
     * @param {Uint8Array} value - 元数据项的值。
     * @returns {Array} - 返回一个包含两个元素的数组
     *  - 第一个元素表示是否采用小端字节序,第二个元素表示是否采用显式VR格式。
     */
    #getdatametamode(attrcode, value) {
        //获取数据元数据的模式,有两种显示的或隐式的,以及数值的大小端
        if (attrcode != "0002,0010") {
            return [null, null];
        }
        let isLitteEndian = null;
        let isExplicitVR = null;
        switch (value) {
            case "1.2.840.10008.1.2.1\x00"://显示little
                isLitteEndian = true;
                isExplicitVR = true;
                break;
            case "1.2.840.10008.1.2.2\x00"://显示big
                isLitteEndian = false;
                isExplicitVR = true;
                break;
            case "1.2.840.10008.1.2\x00"://隐式little
                isLitteEndian = true;
                isExplicitVR = false;
                break;
            default:
                break;
        }
        return [isLitteEndian, isExplicitVR]
    }


    /**
     * parsedatameta,解析数据元数据。
     * 该方法解析文件中的数据元数据区域,并提取出其中的元数据信息。
     * @returns {Map} 返回一个包含数据元数据信息的 Map 对象。
     */
    #parsedatameta() {
        let datameta = new Map();
        while (this.offset < this.bytearr.length) {
            let metadata = null;
            if (this.isexplicitvr) {
                metadata = this.#getheaderonetag(this.islitteendian);
            }
            else {
                metadata = this.#getoneimplicitmetadata(this.islitteendian);
            }
            datameta.set(metadata.attrcode, metadata);
        }
        return datameta;
    }

    /**
     * getoneimplicitmetadata,解析隐式数据元数据。
     * 该方法解析文件中的隐式数据元数据区域,并提取出其中的元数据信息。
     * @returns {Object} 返回一个包含数据元数据信息的对象。
     *  - {string} attrcode - 元数据项的属性代码。
     *  - {string} vr - 元数据项的VR。
     *  - {number} datalength - 元数据项的值的长度。
     *  - {Uint8Array} data - 元数据项的值。
     *  - {any} value - 根据VR解析后的值。
     */
    #getoneimplicitmetadata() {
        let arr = this.bytearr;
        let offset = this.offset;
        let attrcode = arr.slice(offset, offset += 4);
        attrcode = Array.from(attrcode).map(byte => byte.toString(16).padStart(2, '0'));
        attrcode = attrcode[1] + attrcode[0] + "," + attrcode[3] + attrcode[2];
        let vr = 'UI';
        let datalength = new DataView(arr.buffer.slice(offset, offset += 4)).getInt32(0, true);
        let data = arr.slice(offset, offset += datalength);
        vr = this.attricodetovr(attrcode);
        let value = this.vrparse(vr, data, this.islitteendian);
        this.offset = offset;
        return { attrcode, vr, datalength, data, value }
    }


    /**
     * fromurl,从URL中加载DICM文件。
     * @static
     * @param {string} url - 文件的URL地址。
     * @returns {Promise<DICM>} - 返回一个 Promise,resolve 时返回一个 DICM 对象。
     */
    static async fromurl(url) {
        let bf = await (await fetch(url)).arrayBuffer();
        return new DICM(bf);
    }


    /**
     * vrparse,解析VR。
     * @param {string} vr - VR 的字符串表示。
     * @param {Uint8Array} arr - 要解析的数组。
     * @param {boolean} islitteendian - 是否采用小端字节序。
     * @returns {string | number } - 解析后的值。
     */
    vrparse(vr, arr, islitteendian) {
        switch (vr) {
            case 'CS':
            case 'SH': //short string 
            case 'LO':
            case 'ST':
            case 'LT':
            case 'UT':
            case 'AE': //application entity
            case 'PN': //person name 
            case 'UI': //Unique Identifier
            case 'DA':
            case 'TM':
            case 'DT':
            case 'AS':
            case 'IS':
            case 'DS':
            case 'OW':
            case 'OF':
                return new TextDecoder().decode(arr);
            case 'UL': //unsigned long
                return new DataView(arr.buffer).getUint32(0, islitteendian);
            case 'SS': //signed short 
                return new DataView(arr.buffer).getInt16(0, islitteendian);
            case 'US': //unsigned short
            case 'AT': //attribute tag
                return new DataView(arr.buffer).getUint16(0, islitteendian);
            case 'SL': //signed long
                return new DataView(arr.buffer).getInt32(0, islitteendian);
            case 'FL': //float
                return new DataView(arr.buffer).getFloat32(0, islitteendian);
            case 'FD': //double
                return new DataView(arr.buffer).getFloat64(0, islitteendian);
            case 'OB':
            case 'UN':
                break;
        }
        return 'unknow';
    }

    /**
     * attricodetovr,将属性代码转换为VR。
     * @param {string} attrcode - 属性代码。
     * @returns {string} - 对应的VR。
     */

    attricodetovr(attrcode) {
        switch (attrcode) {
            case "0002,0000"://文件元信息长度
                return "UL";
            case "0002,0010"://传输语法
                return "UI";
            case "0002,0013"://文件生成程序的标题
                return "SH";
            case "0008,0005"://文本编码
                return "CS";
            case "0008,0008":
                return "CS";
            case "0008,1032"://成像时间
                return "SQ";
            case "0008,1111":
                return "SQ";
            case "0008,0020"://检查日期
                return "DA";
            case "0008,0060"://成像仪器
                return "CS";
            case "0008,0070"://成像仪厂商
                return "LO";
            case "0008,0080":
                return "LO";
            case "0010,0010"://病人姓名
                return "PN";
            case "0010,0020"://病人id
                return "LO";
            case "0010,0030"://病人生日
                return "DA";
            case "0018,0060"://电压
                return "DS";
            case "0018,1030"://协议名
                return "LO";
            case "0018,1151":
                return "IS";
            case "0020,0010"://检查ID
                return "SH";
            case "0020,0011"://序列
                return "IS";
            case "0020,0012"://成像编号
                return "IS";
            case "0020,0013"://影像编号
                return "IS";
            case "0028,0002"://像素采样1为灰度3为彩色
                return "US";
            case "0028,0004"://图像模式MONOCHROME2为灰度
                return "CS";
            case "0028,0010"://row高
                return "US";
            case "0028,0011"://col宽
                return "US";
            case "0028,0100"://单个采样数据长度
                return "US";
            case "0028,0101"://实际长度
                return "US";
            case "0028,0102"://采样最大值
                return "US";
            case "0028,1050"://窗位
                return "DS";
            case "0028,1051"://窗宽
                return "DS";
            //https://dicom.innolitics.com/ciods/ct-image/clinical-trial-subject
            //https://blog.csdn.net/m0_37477175/article/details/103803321
            case "0028,1052": //Rescale Intercept
                return "DS";
            case "0028,1053": //Rescale Slope
                return "DS";
            case "0040,0008"://文件夹标签
                return "SQ";
            case "0040,0260"://文件夹标签
                return "SQ";
            case "0040,0275"://文件夹标签
                return "SQ";
            case '7fe0,0000': //像素长度
                return "UL";
            case "7fe0,0010"://像素数据开始处
                return "OB";
            default:
                return "UI";
        }
    }

    /**
     * getdata,获取图像数据。
     * @returns {Object} - 包含图像数据信息的对象。
     *  - height {number} - 图像高度。
     *  - width {number} - 图像宽度。
     *  - channel {number} - 图像通道数。
     *  - array {Array} - 图像数据数组。
     */
    getdata() {
        let b = this.datameta.get("0028,1052").value; //Rescale Intercept
        let a = this.datameta.get("0028,1053").value;//Rescale Slope
        let v = new Int16Array(this.datameta.get("7fe0,0010").data.buffer);
        a = parseInt(a);
        b = parseInt(b);
        v = v.map(x => x * a + b); //change to HU;
        let height = this.datameta.get("0028,0010").value; //image height;
        let width = this.datameta.get("0028,0011").value;
        return { height, width, channel: 1, array: v, unit: 'HU' }
    }
    /**
     * toImgArray,将图像数据转换为ImgArray对象。
     * @returns {ImgArray} - 包含图像数据的ImgArray对象。
     */
    toImgArray() {
        let v = this.getdata();
        return ImgArray.fromArray(v.array, v);
    }
}



/**
 * delay,创建一个异步的延时函数。
 * @param {number} n - 延时的毫秒数。
 * @returns {Promise} - 返回一个Promise对象,在指定时间后解析。
 */
function delay(n) {
    //异步的延时单位是ms
    return new Promise(function (resolve) {
        setTimeout(resolve, n);
    });
}

//当将该文件用于ES6 Module时,取消以下注释,导出Img, ImgArray 和DICM类
//export {Img, ImgArray,DICM} ;