//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} ;