Почему в этом эксперименте с понижающей выборкой мои прямоугольные местоположения неверны? - PullRequest
0 голосов
/ 10 октября 2019

Я пытаюсь создать заметную записную книжку, которая преобразует входное изображение в определенное пользователем количество ячеек. Пример эксперимента можно увидеть здесь: https://observablehq.com/d/dc19b499ca269533

К сожалению, при меньшем количестве ячеек (см. Ниже 50x50) расположение прямоугольника в большинстве левых ячеек, по-видимому, вычислено неправильно. Он рисует вид артефакта треугольной формы (см. В приложении).

Мне интересно, имеет ли это отношение к ошибкам округления с плавающей точкой Javascript с умножением и делением) Или если у меня где-то есть неправильная логика.

original image downsampled image

Я добавил указанный ниже код (используя 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
        );    
     }))
})()
...