如何解决数组数组中最近的数组
const colors = [
[0,10,56],[40,233,247],[50,199,70],[255,0],...
];
const reduceColor = pix => {
let lowdist = Number.MAX_SAFE_INTEGER;
let closestColor;
for (let color of colors) {
const dist = Math.abs(pix[0] - color[0]) +
Math.abs(pix[1] - color[1]) +
Math.abs(pix[2] - color[2]) ;
if (dist<lowdist) {
lowdist = dist;
closestColor = color;
}
}
return closestColor;
}
console.log(reduceColor([240,30]));
// [255,0]
哪个工作很好,但在整个图像的上下文中很慢。
有没有办法在提供数组时检查另一个数组(由子数组组成)中最近的子数组,而不必迭代和检查每个子数组?
解决方法
我认为没有更好的解决方案,您必须检查每一行,但是如果您充分利用其内置方法,您可以使用通常具有更快实现的 Typed Arrays。通常浏览器在音频处理或画布渲染中使用这些类型化数组。
一定要避免使用 JS 数组,因为它们在内存中不是连续的,对它们进行大量操作可能会非常慢。类型化数组是连续的。
还要避免使用 Math 方法,因为它们是通过 JS 解释器对象的抽象实现的,所以 Math.abs
可能会很慢(它应该处理大数、浮点数、字符串、null、NaN 等) .在这些情况下,数值运算会更快。
对于解决方案,您可以使用 Uint32Array
(MDN),将颜色重新解释为 32 位数字。在十六进制中,我们用 2 个十六进制数字表示一个字节,因此 32 位是 8 位数字。我们将它们存储在数组中:
-- RR GG BB
0x 00 00 00 00
function makeColor(r,g,b) {
// the 32-bit color will be "0x00RRGGBB" in hex
return (r << 16) + (g << 8) + b;
}
function parseColor(color) {
// isolating the corrispondent bit of the colors
return [ (color & 0xff0000) >> 16,(color & 0xff00) >> 8,color & 0xff ];
}
function makeColorArray(colors) {
// creating the Uint32Array from the colors array
return new Uint32Array(colors.map(c => makeColor(...c)));
}
function distanceRGB(p1,p2) {
let r1 = (p1 & 0xff0000) >> 16,// extract red p1
g1 = (p1 & 0xff00) >> 8,// extract green p1
b1 = p1 & 0xff,// extract blue p1
r2 = (p2 & 0xff0000) >> 16,// extract red p2
g2 = (p2 & 0xff00) >> 8,// extract green p2
b2 = p2 & 0xff,// extract blue p2
rd = r1 > r2 ? (r1 - r2) : (r2 - r1),// compute dist r
gd = g1 > g2 ? (g1 - g2) : (g2 - g1),// compute dist g
bd = b1 > b2 ? (b1 - b2) : (b2 - b1); // compute dist b
return rd + gd + bd; // sum them up
}
// rising the threshold can speed up the process
function findNearest(colors,distance,threshold) {
return function(pixel) {
let bestDist = 765,best = 0,curr; // 765 is the max distance
for (c of colors) {
curr = distance(pixel,c);
if (curr <= threshold) { return c; }
else if (curr < bestDist) {
best = c;
bestDist = curr;
}
}
return best;
};
}
const colors = makeColorArray([
[0,10,56],[40,233,247],[50,199,70],[255,0],//... other colors
]);
const image = makeColorArray([
[240,30]
//... other pixels
])
const nearest = Array.from(image.map(findNearest(colors,distanceRGB,0))).map(parseColor)
// ===== TEST =====
function randomColor() {
return [ Math.floor(Math.random() * 255),Math.floor(Math.random() * 255),Math.floor(Math.random() * 255) ];
}
testImages = [];
for (let i = 0; i < 1000000; ++i) { testImages.push(randomColor()); }
testImages = makeColorArray(testImages)
testColors = [];
for (let i = 0; i < 1000; ++i) { testColors.push(randomColor()); }
testColors = makeColorArray(testColors);
// START
let testNow = Date.now();
Array.from(testImages.map(findNearest(testColors,0))).map(parseColor)
console.log(Date.now() - testNow)
我已经用 4 种颜色对超过一百万个随机像素 (1000x1000) 进行了测试,耗时不到一秒(~250-450 毫秒),一千种测试颜色需要 12-15 秒。用普通阵列做同样的实验只用 4 种颜色就需要 4-6 秒,我没有尝试千色测试(显然都是在我的电脑上)。
考虑将繁重的工作交给 Worker (MDN) 以避免 UI 冻结。
我不知道在更大的图像上运行是否足够。我相信它可以进一步优化(也许通过格雷码汉明距离),但顺便说一下,这是一个很好的起点。
您也可能有兴趣获得一种从图像中提取像素值的方法,只需检查 <img>
或 <canvas>
元素中的 ImageData (MDN) 和 how to retrive it (Stack Overflow)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。