高级搜索

This commit is contained in:
2022-04-18 18:47:22 +08:00
parent 7c299748c3
commit 4a7f7b316f
26 changed files with 1584 additions and 84 deletions
+86
View File
@@ -0,0 +1,86 @@
module.exports = function(lab1, lab2){
var rgb2labArray1 = lab1;
var rgb2labArray2 = lab2;
var l1 = rgb2labArray1[0];
var a1 = rgb2labArray1[1];
var b1 = rgb2labArray1[2];
var l2 = rgb2labArray2[0];
var a2 = rgb2labArray2[1];
var b2 = rgb2labArray2[2];
var avg_lp = (l1 + l2) / 2;
var c1 = Math.sqrt(Math.pow(a1, 2) + Math.pow(b1, 2));
var c2 = Math.sqrt(Math.pow(a2, 2) + Math.pow(b2, 2));
var avg_c = (c1 + c2) / 2;
var g = (1- Math.sqrt(Math.pow(avg_c, 7) / (Math.pow(avg_c, 7) + Math.pow(25, 7)))) / 2;
var a1p = a1 * (1 + g);
var a2p = a2 * (1 + g);
var c1p = Math.sqrt(Math.pow(a1p, 2) + Math.pow(b1, 2));
var c2p = Math.sqrt(Math.pow(a2p, 2) + Math.pow(b2, 2));
var avg_cp = (c1p + c2p) / 2;
var h1p = rad2deg(Math.atan2(b1, a1p));
if(h1p < 0){
h1p = h1p + 360;
}
var h2p = rad2deg(Math.atan2(b2, a2p));
if(h2p < 0){
h2p = h2p + 360;
}
var avg_hp = Math.abs(h1p - h2p) > 180 ? (h1p + h2p + 360) / 2 : (h1p + h1p) / 2;
var t = 1 - 0.17 * Math.cos(deg2rad(avg_hp - 30)) + 0.24 * Math.cos(deg2rad(2 * avg_hp)) + 0.32 * Math.cos(deg2rad(3 * avg_hp + 6)) - 0.2 * Math.cos(deg2rad(4 * avg_hp - 63))
var delta_hp = h2p - h1p;
if(Math.abs(delta_hp) > 180){
if (h2p <= h1p) {
delta_hp += 360;
}
else {
delta_hp -= 360;
}
}
var delta_lp = l2 - l1;
var delta_cp = c2p - c1p;
delta_hp = 2 * Math.sqrt(c1p * c2p) * Math.sin(deg2rad(delta_hp) / 2);
var s_l = 1 + ((0.015 * Math.pow(avg_lp - 50, 2)) / Math.sqrt(20 + Math.pow(avg_lp - 50, 2)));
var s_c = 1 + 0.045 * avg_cp
var s_h = 1 + 0.015 * avg_cp * t;
var delta_ro = 30 * Math.exp( - (Math.pow((avg_hp - 275) / 25, 2)));
var r_c = 2 * Math.sqrt(Math.pow(avg_cp, 7) / (Math.pow(avg_cp, 7) + Math.pow(25, 7)));
var r_t = -r_c * Math.sin(2 * deg2rad(delta_ro));
var kl = 1, kc =1, kh = 1;
var delta_e = Math.sqrt(Math.pow(delta_lp / (kl * s_l), 2) + Math.pow(delta_cp / (kc * s_c), 2) + Math.pow(delta_hp / (kh * s_h), 2) + r_t * (delta_cp / (kc * s_c)) * (delta_hp / (kh * s_h)))
return delta_e
function rad2deg(rad){
return 360 * rad / (2 * Math.PI);
}
function deg2rad(deg){
return (2 * Math.PI * deg) / 360;
}
}
+13
View File
@@ -0,0 +1,13 @@
import LabCom from './lab'
import XyzCom from './xyz'
import ColorDiff from './colorDiff'
export const toRgb = (lab) => {
let xyz = LabCom.xyz(lab)
return XyzCom.rgb(xyz)
}
export const Ediff = (lab1, lab2) => {
return ColorDiff(lab1, lab2)
}
+54
View File
@@ -0,0 +1,54 @@
var xyz = require('./xyz');
module.exports = {
name: 'lab',
min: [0,-100,-100],
max: [100,100,100],
channel: ['lightness', 'a', 'b'],
alias: ['LAB', 'cielab'],
xyz: function(lab) {
var l = lab[0],
a = lab[1],
b = lab[2],
x, y, z, y2;
if (l <= 8) {
y = (l * 100) / 903.3;
y2 = (7.787 * (y / 100)) + (16 / 116);
} else {
y = 100 * Math.pow((l + 16) / 116, 3);
y2 = Math.pow(y / 100, 1/3);
}
x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
return [x, y, z];
}
};
//extend xyz
xyz.lab = function(xyz){
var x = xyz[0],
y = xyz[1],
z = xyz[2],
l, a, b;
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
l = (116 * y) - 16;
a = 500 * (x - y);
b = 200 * (y - z);
return [l, a, b];
};
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
name: 'rgb',
min: [0,0,0],
max: [255,255,255],
channel: ['red', 'green', 'blue'],
alias: ['RGB']
};
+138
View File
@@ -0,0 +1,138 @@
var rgb = require('./rgb');
var xyz = {
name: 'xyz',
min: [0,0,0],
channel: ['X','Y','Z'],
alias: ['XYZ', 'ciexyz', 'cie1931']
};
/**
* Whitepoint reference values with observer/illuminant
*
* http://en.wikipedia.org/wiki/Standard_illuminant
*/
xyz.whitepoint = {
//1931 2°
2: {
//incadescent
A:[109.85, 100, 35.585],
// B:[],
C: [98.074, 100, 118.232],
D50: [96.422, 100, 82.521],
D55: [95.682, 100, 92.149],
//daylight
D65: [95.045592705167, 100, 108.9057750759878],
D75: [94.972, 100, 122.638],
//flourescent
// F1: [],
F2: [99.187, 100, 67.395],
// F3: [],
// F4: [],
// F5: [],
// F6:[],
F7: [95.044, 100, 108.755],
// F8: [],
// F9: [],
// F10: [],
F11: [100.966, 100, 64.370],
// F12: [],
E: [100,100,100]
},
//1964 10°
10: {
//incadescent
A:[111.144, 100, 35.200],
C: [97.285, 100, 116.145],
D50: [96.720, 100, 81.427],
D55: [95.799, 100, 90.926],
//daylight
D65: [94.811, 100, 107.304],
D75: [94.416, 100, 120.641],
//flourescent
F2: [103.280, 100, 69.026],
F7: [95.792, 100, 107.687],
F11: [103.866, 100, 65.627],
E: [100,100,100]
}
};
/**
* Top values are the whitepoint’s top values, default are D65
*/
xyz.max = xyz.whitepoint[2].D65;
/**
* Transform xyz to rgb
*
* @param {Array} xyz Array of xyz values
*
* @return {Array} RGB values
*/
xyz.rgb = function (_xyz, white) {
//FIXME: make sure we have to divide like this. Probably we have to replace matrix as well then
white = white || xyz.whitepoint[2].E;
var x = _xyz[0] / white[0],
y = _xyz[1] / white[1],
z = _xyz[2] / white[2],
r, g, b;
// assume sRGB
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
r = (x * 3.240969941904521) + (y * -1.537383177570093) + (z * -0.498610760293);
g = (x * -0.96924363628087) + (y * 1.87596750150772) + (z * 0.041555057407175);
b = (x * 0.055630079696993) + (y * -0.20397695888897) + (z * 1.056971514242878);
r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
: r = (r * 12.92);
g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
: g = (g * 12.92);
b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
: b = (b * 12.92);
r = Math.min(Math.max(0, r), 1);
g = Math.min(Math.max(0, g), 1);
b = Math.min(Math.max(0, b), 1);
return [r * 255, g * 255, b * 255];
}
/**
* RGB to XYZ
*
* @param {Array} rgb RGB channels
*
* @return {Array} XYZ channels
*/
rgb.xyz = function(rgb, white) {
var r = rgb[0] / 255,
g = rgb[1] / 255,
b = rgb[2] / 255;
// assume sRGB
r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
var x = (r * 0.41239079926595) + (g * 0.35758433938387) + (b * 0.18048078840183);
var y = (r * 0.21263900587151) + (g * 0.71516867876775) + (b * 0.072192315360733);
var z = (r * 0.019330818715591) + (g * 0.11919477979462) + (b * 0.95053215224966);
white = white || xyz.whitepoint[2].E;
return [x * white[0], y * white[1], z * white[2]];
};
module.exports = xyz;
+146
View File
@@ -0,0 +1,146 @@
import { uint32ToUint8Array, uint8ArrayToHex } from "./utils";
export class Command {
// 测量序号
static measureId = 1;
// 命令完整响应的长度
responseSize = 0;
// 命令发送的数据
content = new Uint8Array(0);
// 命令响应的数据
response = new Uint8Array(0);
// 等待响应的超时时间
timeout = 3000;
// 发送的数据是否需要生成和校验值
needSign = true;
/**
* @param {Uint8Array|ArrayBuffer|number[]} content
* @param {number} responseSize
* @param {number} timeout
* @param {boolean} needSign
*/
constructor(content, responseSize, timeout = 3000, needSign = true) {
if (content instanceof Uint8Array) {
this.content = content;
} else {
this.content = new Uint8Array(content);
}
this.responseSize = responseSize;
if (typeof timeout === 'number' && timeout >= 0) {
this.timeout = timeout;
}
this.needSign = needSign;
}
/**
* 返回一个 ArrayBuffer 数组, 用于发送
* @returns {ArrayBuffer[]}
*/
get data() {
if (this.content.length === 0) throw new Error('正文内容不能为空');
const data = [];
const b = new Uint8Array(this.content.buffer);
if (this.needSign) {
b[b.length - 1] = Command.getSign(b);
}
for (let i = 0; i < b.length; i += 20) {
data.push(b.slice(i, i + 20).buffer);
}
return data;
}
/** 是否接收完成 */
get isComplete() {
return this.response.length >= this.responseSize;
}
/** 是否有效 */
get isValid() {
return Command.getSign(this.response) === this.response[this.response.length - 1];
}
/**
* 填充响应数组
* @param {ArrayBuffer} buffer
*/
fillResponse(buffer) {
this.response = new Uint8Array([...this.response, ...(new Uint8Array(buffer))]);
}
/**
* 获取和校验值
* @param {ArrayBuffer|Uint8Array} buffer
*/
static getSign(buffer) {
const _b = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
let sum = 0;
_b.slice(0, _b.length - 1).forEach(i => sum += i);
return new Uint8Array([sum])[0];
}
// 唤醒命令
static WakeUp = new Command([0xf0], 0, 0, false);
/**
* 获取测量命令
* @param {number} mode
*/
static measure(mode = 0) {
Command.measureId += 1;
const measureId = uint32ToUint8Array(Command.measureId);
return new Command([0xbb, 1, mode, ...measureId, 0, 0xff, 0], 10, 1500);
}
/**
* 获取测量数据 (Lab)
* @param {number} mode
*/
static getLab(mode = 0) {
return new Command([0xbb, 3, mode, 0, 0, 0, 0, 0, 0xff, 0], 20, 1500);
}
/**
* 获取测量数据 (RGB)
* @param {number} mode
*/
static getRGB(mode = 0) {
return new Command([0xbb, 4, mode, 0, 0, 0, 0, 0, 0xff, 0], 20, 1500);
}
/**
* 获取测量的光谱数据
* @param {number} mode
*/
static getSpectral(mode = 0) {
return new Command([0xbb, 2, 0x10 + mode, 0, 0, 0 ,0 ,0, 0xff, 0], 200, 5000);
}
/**
* 白校准
* @param {number} check 是否判断校准成功 1 判断 0 不判断
*/
static whiteCalibrate(check = 1) {
return new Command([0xbb, 0x11, check, 0, 0, 0, 0, 0, 0xff, 0], 10, 1500);
}
/**
* 黑校准
* @param {number} check 是否判断校准成功
*/
static blackCalibrate(check = 1) {
return new Command([0xbb, 0x10, check, 0, 0, 0, 0, 0, 0xff, 0], 10, 1500);
}
/** 获取校准状态 */
static GetCalibrationInf = new Command([0xbb, 0x1e, 0, 0, 0, 0, 0, 0, 0xff, 0], 20, 1500);
static GetDeviceInf = new Command([0xbb, 0x12, 0x01, 0, 0, 0, 0, 0, 0xff, 0], 200, 5000);
}
+70
View File
@@ -0,0 +1,70 @@
/**
* Uint32 转 Uint8 数组
* @param {number} n
*/
export function uint32ToUint8Array(n) {
return new Uint8Array(new Uint32Array([n]).buffer);
}
/**
* Uint8 数组 转 Float32
* @param {Uint8Array} raw
*/
export function uint8ArrayToFloat32(raw) {
return new Float32Array(raw.buffer)[0];
}
/**
* Uint8 数组 转 Uint16
* @param {Uint8Array} raw
*/
export function uint8ArrayToUint16(raw) {
return new Uint16Array(raw.buffer)[0];
}
/**
* Uint8 数组转 Uint32
* @param {Uint8Array} raw
* @returns
*/
export function uint8ArrayToUnit32(raw) {
return new Uint32Array(raw.buffer)[0];
}
/**
* 等待指定时长
* @param {number} duration
*/
export function waitFor(duration) {
return new Promise(resolve => {
setTimeout(resolve, duration);
});
}
/**
* uint8 数组转 hex 字符串
* @param {Uint8Array} raw
*/
export function uint8ArrayToHex(raw) {
const s = [];
raw.forEach(i => {
const b = i.toString(16);
s.push(b.length > 1 ? b : `0${b}`);
});
return s.join(' ');
}
// 二进制转字符串(ascii)
export function bufferToString(buffer) {
let str = "";
for (let code of buffer) {
if (code === 0) break;
str += utf82string(code);
}
return str;
}