0

I would like to detect the width of the icon (right-pointing hand) inside the following image.

enter image description here

I suppose the possible solution might involve color removal illustrated in this approach, which can be used to remove the lightblue background color in this case. But I'm not sure about the next step after color removal.

Ian Y.
  • 2,293
  • 6
  • 39
  • 55
  • I guess I found [a solution](https://stackoverflow.com/a/12178531/1091014). I will try to implement it with that solution, but any better solution is welcomed. – Ian Y. Jul 16 '17 at 04:28

1 Answers1

1

I figured out something!

Here's how I did it:

  1. add the image to the document using an <img> tag (either through plain ole-html or js)
  2. create a 2D canvas element and make it's size the same size as the img
  3. draw the image onto the canvas using context.drawImage
  4. use context.getImageData to get the colors of the pixels in the canvas
  5. transform this raw array of number to hex string colors
  6. group the colors into rows by the rows of the image
  7. grab the first color of the image as the colorToFilter
  8. create minX, minY, maxX, and maxY variables to keep track of the pixels that aren't the colorToFilter
  9. iterate through every pixel in the pixelMatrix checking whether the the current row or column is less than or greater than colors that aren't the colorToFilter
  10. take the difference of maxX - minX and maxY - minY and that's your dimensions!

FYI: I'm using a data uri for the image src because of cross-origin issues.

Let me know if you need more explanation. This solution may not be the most efficient but it works!

/**
 * converts a number from 0-255 to a hex number padded with a zero if necessary
 * @param {number} number 
 */
function toHex(number) {
  const numberAsString = Number(number).toString(16);
  return numberAsString.length === 1 ? '0' + numberAsString : numberAsString;
}

/**
 * Grabs the pixel colors from an image and stores it in a matrix
 * @param {HTMLImageElement} img 
 */
function convertImageToPixelData(img) {
  // credit for getting the color of pixel comes from here:
  // https://stackoverflow.com/questions/8751020/how-to-get-a-pixels-x-y-coordinate-color-from-an-image
  const canvas = document.createElement('canvas');
  const imgWidth = img.clientWidth;
  const imgHeight = img.clientHeight;
  canvas.width = imgWidth;
  canvas.height = imgHeight;
  const context = canvas.getContext('2d');
  context.drawImage(img, 0, 0, imgWidth, imgHeight);

  const rawPixelData = context.getImageData(0, 0, imgWidth, imgHeight).data;

  // convert to hex string
  const pixelColors = rawPixelData.reduce((groups, pixelColor, index) => {
    if (index % 4 === 3) { // skip the alpha channel
      return groups;
    }
    if (groups[groups.length - 1].length === 6) {
      groups.push('');
    }
    groups[groups.length - 1] += toHex(pixelColor);
    return groups;
  }, ['']);

  // convert to matrix
  /**
   * @type {string[][]}
   */
  const pixelMatrix = pixelColors.reduce((matrix, pixel) => {
    const currentRow = matrix[matrix.length - 1];
    if (currentRow.length < imgWidth) {
      currentRow.push(pixel);
    } else {
      matrix.push([pixel]);
    }
    return matrix;
  }, [[]]);
  return pixelMatrix;
}

/**
 * finds the dimensions of an icon inside a single colored image
 * @param {HTMLImageElement} img 
 */
function findBox(img) {
  const pixelMatrix = convertImageToPixelData(img);
  const colorToFilter = pixelMatrix[0][0];

  const imgWidth = img.clientWidth;
  const imgHeight = img.clientHeight;

  let maxX = 0;
  let minX = imgWidth;
  let maxY = 0;
  let minY = imgHeight;

  // for each pixel calculate if the index of the pixel is greater than the current max or min
  for (let row = 0; row < imgHeight; row += 1) {
    for (let column = 0; column < imgWidth; column += 1) {
      const pixel = pixelMatrix[row][column];
      if (pixel !== colorToFilter) {
        if (column < minX) {
          minX = column;
        }
        if (column > maxX) {
          maxX = column;
        }
        if (row < minY) {
          minY = row;
        }
        if (row > maxY) {
          maxY = row;
        }
      }
    }
  }

  const width = maxX - minX + 1;
  const height = maxY - minY + 1;

  return { width, height };
}

const img = document.querySelector('#my-image');

const box = findBox(img);
console.log(box);
<img id="my-image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABkCAIAAADrOV6nAAAFUklEQVR42u3caVPaXByG8X7/D+AuakXBBQVFgiRhJyAgCpElICSAJkFQcFdQLKCt6/NYW8LWe+Z6pTOO429O+J+TxG+6hIj6um/4E4AQgRCBEIT4K4AQgRCBEIQIhAiECIQgRCBEIEQgBCECIQIhAiEIEQgRCBEIQYhA2DvlyQglOqclSiV5LKE4CPuntVggtbUkUyMiMSSaJ4u+leskUdv3JTgQ9nLxfVfYnvcuyNRoU44Yke3q8o7xTqAfsk9VMwkQ9thSC5hEz4LsmJbJR7YhyTJ55NJUwut3/LPcc/weCHskjvPOSubGRXJUts+WGO1pSHcRJWpp6gO2F92kQNgLRd2ifay55kj1WfITs1cJ+mIgDsIuT5juHZ3cWHzEsOxcvhXor/gZK475HItxppvTSjTjUUnNCXOiFDLVs1/wqyd0JXpY8mytYlPRtcklapXo1g6Bnjv/ysXznls7dasan5qS3ezDvrBbF89AUNu6eI4UmNXq71w8+c0bVldmZgqPY6p5QmR8loH262HC+HbeOd5imDqJmP//4lkXTJd+TdE2JrV2Fw1yyarJMh579AAHbN3JtLsuk8PNrZ5Nc/m7F0/yLrpSdk1KxHjOG1mNZcyRpDkSdfo9ga1WXjLmWE+04pyGDD2VJ191QKoOP+rAsZx3PabPBax8nHVxhyD8bNvXXEljRz5D7SuTy9NHIG+5SxHVpOUyYihvr75rpbGPPPu8pTKjLrmbFe2TMjUubw6LT0u8saWZKYV3LCD8aHJxSrbW5GKZLrObD1/3UzbBUkuuXwS1ReuoaFYV2QwIX/uFjbKldU5mX7zm6Z7ze7PWo4ulLZsVhC+GF3vLb7jA/MnFsyvVUsw2CH+141U1159L1y9+rfwsCH/mlajGEpyqcH3k1/hoDLlB+FSMkBsjjGP5rp+WIH2b4TDO/CyibxAWfMY+8qsLARabiq4QVlndOWu+4R1V4VfWL/4Q2zUf8XDY2r9sz6g04T2nr7i+N+9yJFbLzgmJ1PBs/r9+Hz0nWJP8y/z7bCrdaC+UFEw4YPsoSilCfvM6vHhsG5cd2hcHddRt8LtE63dwRtq+giI9JLpW2rxviy6duNUlRnseJ999l7rwzIosCNt3U0lwDon2xWonRxLOUNgDYfsKN7b25PxVR6dKl5gCYRsPSIPzIjFzmv4Xz1YG5Yx0RycR48dsB084+ZgehG0/oCkGLR3xs1b5XR/u2rc7j0QOycyaAvuKjXPf/LFfX8vS19tzJe/6/sA9jdEjhKmco83HpPW08YyZkqmZ8vOjN1Q1ojmyzGR3QahAMc9424bStPHUqy6558rba++fe6vHtAUXvQbCtkf51Z0aSqmr4EYYhMoMpaPH4Y5MpPveXRAqNJQeBYhOEPIRAoQK5BfpIcmj78Cm4jSdwzijSBnXsGhduFXWz1lJp/XYVCg50cyeCcps5wW/mE7YB/GN+156mpvdkNs00bTu8aoKTu0FRz4IpnPfbIGalgKRtQQIlS2es/3VGc39/vpFaKn5ZgU9W3nzMo1gPnNNS7sgVPqFmKBGojRXf/XMPFFNbt5//F1DJZwGodLt5unRUphSaJyppA9BqHirYYNs1d7gNm9fv19oDy4UfMa6As9fMxhnOpYltHEUIdtK6DlMDeBVtLfftY/nHKmwzDM3gqOatf75Xaes8yLTWw/v/jv/LuFNh6bmg7mJeJrlM4ET3n/28ePY1lrr6zeCr5hhQ8mcPjGweH1HiEAIQgRCBEIEQhAiECIQIhCCEIEQgRCBEIQIhAiECIQgRCBEIEQgBCECIQIhAiEIUe/0A1MOqyYF+UAcAAAAAElFTkSuQmCC">
Rico Kahler
  • 17,616
  • 11
  • 59
  • 85