Я пытаюсь создать заметную записную книжку, которая преобразует входное изображение в определенное пользователем количество ячеек. Пример эксперимента можно увидеть здесь: https://observablehq.com/d/dc19b499ca269533
К сожалению, при меньшем количестве ячеек (см. Ниже 50x50) расположение прямоугольника в большинстве левых ячеек, по-видимому, вычислено неправильно. Он рисует вид артефакта треугольной формы (см. В приложении).
Мне интересно, имеет ли это отношение к ошибкам округления с плавающей точкой Javascript с умножением и делением) Или если у меня где-то есть неправильная логика.
Я добавил указанный ниже код (используя iife для соответствия наблюдаемому поведению), но для копанияу вас захочется посетить оригинальный блокнот.
const data = (() => {
ctx.drawImage(img, 0, 0);
ctx.beginPath();
const columnWidth = img.naturalWidth / columns;
const rowWidth = img.naturalHeight / rows;
const getCellData = (rowIndex) =>
[...Array(columns)]
.map((_, columnIndex) => {
const imgData = ctx.getImageData(
columnIndex * columnWidth,
rowIndex * rowWidth,
columnWidth,
rowWidth
);
// each chunk represents a color
return lodash.chunk(imgData.data, 4);
});
return [...Array(rows)]
.map((_, rowIndex) => getCellData(rowIndex));
});
const averagedData = (() => {
const getCellAverage = (pixels) => pixels
.reduce((
[sumR, sumG, sumB, sumA],
[r,g,b,a]
) => [
sumR + r,
sumG + g,
sumB + b,
sumA + a
] , [0,0,0,0])
.map(v => v / pixels.length);
return data.map(row => row.map(getCellAverage))
})()
(()=>{
const columnWidth = img.naturalWidth / columns;
const rowWidth = img.naturalHeight / rows;
ctx.clearRect(0,0,canvas.width, canvas.height);
averagedData
.forEach((row, rowIndex) => row
.forEach(([r,g,b,a], columnIndex) => {
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a})`;
ctx.strokeStyle = 'red';
ctx.fillRect(
columnWidth * columnIndex,
rowWidth * rowIndex,
columnWidth, columnIndex
);
showBorder && ctx.strokeRect(
columnWidth * columnIndex,
rowWidth * rowIndex,
columnWidth, columnIndex
);
}))
})()