0

I am trying to extract only white text from the image using pixel manipulation but it does not work on some images. It works on https://prnt.sc/24q29qu but not on https://prnt.sc/24pymne Please see the code used and you may just paste the code in the console to see the output. Tested it on chrome browser. You may click on the output in the console to see the image output.

    var imageSource = document.querySelector("body > div.image-constrain.js-image-wrap > div > div > img")

    var width = 2*imageSource.width;
    var height = 2*imageSource.height;

    var img = new Image();
    img.crossOrigin = 'anonymous';
    img.src = imageSource.src;
    var c = document.createElement("canvas")
    c.width = width;
    c.height = height;
    var ctx = c.getContext("2d");
    ctx.drawImage(img, 0, 0);

    var imageData = ctx.getImageData(0, 0, width, height);
    var data = imageData.data;
    console.log(data);
    // Replacing All the pixels except white
    for (let i = 0; i < data.length; i += 4) {
        if(data[i] != 255) {
            data[i] = 0;
        }
        if(data[i+1] != 255) {
            data[i+1] = 0;
        }
        if(data[i+2] != 255) {
            data[i+2] = 0;
        }
        if(data[i+3] != 255) {
            data[i+3] = 0;
        }
    }

    console.log(imageData.data);  
     ctx.putImageData(imageData, 0, 0);


        img.src = c.toDataURL();
        pixelatedImage = c.toDataURL();
        console.log(pixelatedImage);
engageub
  • 13
  • 4
  • 1
    What does "doesn't work" mean. Your image links seem to be dead, and [you must wait for your images have loaded before doing anything with it](https://stackoverflow.com/questions/32880641/canvascontext2d-drawimage-issue-onload-and-cors). – Kaiido Dec 27 '21 at 12:05
  • The image links are working fine. The source of the images are not url's but strings, so I did not use img.onload function. I tried to add that in the code now but it still does not show the output as expected. After the imageData is changed the following line of code shows similar looking original image instead of expected output. "img.src = c.toDataURL();" For the other image it works as expected. – engageub Dec 27 '21 at 14:10
  • You will understand better when you paste the code in console. – engageub Dec 27 '21 at 14:15
  • 1
    No, your links point to some weird webpage, not to images, pasting your code in the console wouldn't do much because we don't have an element matching the first selector. And whatever the source, you must wait for the image has loaded before doing anything with it. – Kaiido Dec 28 '21 at 01:19
  • It points to a webpage where image is present. The website only hosts images. Here is the snapsnot how the webpage looks. You need to paste the code in console. https://i.ibb.co/WWbR316/image.png – engageub Dec 28 '21 at 10:49
  • 1
    [This](https://i.stack.imgur.com/kAWHl.png) is how these pages render for me. And once again, for your code to work there needs to be an element matching the `"body > div.image-constrain.js-image-wrap > div > div > img"` selector in the page where the console has been opened, on this very page there is no such element. Moreover this element would need to have its `src` set to a valid image resource. None of your links do point to such an image resource, they do point to HTML pages (even for you). – Kaiido Dec 28 '21 at 11:14
  • The elements are still present. Here is the snapshot https://i.ibb.co/D9jNWD3/image.png . It looks like you have some ad blocker or your browser is blocking. Can you try to open using other browser or use proxy to open the link. – engageub Dec 28 '21 at 11:34
  • 1
    The onus is on you to provide a [mcve]. Instead of posting screenshots of the page showing the screenshot, why don't you post the image directly on this other service? And no, the element matching this selector never has been on [this page](https://stackoverflow.com/questions/70491557/pixel-manipulation-does-not-work-on-some-images?noredirect=1#comment124633694_70491557). Go ahead, open the console on this page and paste your code. – Kaiido Dec 28 '21 at 11:38
  • You need to paste it on https://prnt.sc website and not stackoverflow. I gave the perfect examples in this question where the links work fine for me. I also shared a screenshot to show that the element exists in the document by pasting in the console https://i.ibb.co/D9jNWD3/image.png . However since the images are not shown to you, I uploaded to imgbb. Steps to reproduce. 1. Visit https://i.ibb.co/fCkScQV/image.png and paste the following code in the console. You will see the output of image. Copy that to your browser url to see the base64 image. – engageub Dec 28 '21 at 13:12
  • 1. Visit https://i.ibb.co/fCkScQV/image.png and paste the following code https://justpaste.it/886eu in the console. You will see the output of image. Copy that to your browser url to see the base64 image. This is a working example. 2. For this image the pixels are not changed https://i.ibb.co/ZKnmnT6/image.png – engageub Dec 28 '21 at 13:18
  • Please [edit] your question to include these instead. – Kaiido Dec 28 '21 at 15:45

1 Answers1

0

Your code is not targeting white pixels, it is targeting each Red Green Blue and Alpha channels that are not 255 and set these channels to 0.

This means that if you have a red #FF0000 pixel, it will stay unchanged (Red is #FF (255) and thus won't be targeted, G & B are already 0, and Alpha is 255 too, so untargeted.

In your image, the Green channel of the green part is 255, so it's untargeted. However its Red channel is neither 0 nor 255, and you can see that the green color actually does change.

(async() => {
  const resp = await fetch("https://i.ibb.co/ZKnmnT6/image.png");
  if (!resp.ok) {
    throw resp.status;
  }
  const blob = await resp.blob();
  const image = await createImageBitmap(blob);
  const canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(image, 0, 0);
  document.body.append(canvas);
  const [r,g,b,a] = ctx.getImageData(0, 0, 1, 1).data;
  console.log({r,g,b,a});
})().catch(console.error);

To target white pixels, you need to check that the four RGBA channels are all 255, not one at a time.

(async() => {
  const resp = await fetch("https://i.ibb.co/ZKnmnT6/image.png");
  if (!resp.ok) {
    throw resp.status;
  }
  const blob = await resp.blob();
  const image = await createImageBitmap(blob);
  const canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(image, 0, 0);
  document.body.append(canvas);
  const img = ctx.getImageData(0, 0, image.width, image.height);
  const imgdata = img.data;
  for (let i = 0; i<imgdata.length; i+=4) {
    if (
      imgdata[i  ] !== 255 ||
      imgdata[i+1] !== 255 ||
      imgdata[i+2] !== 255 ||
      imgdata[i+3] !== 255
    ) {
      imgdata[i  ] =
      imgdata[i+1] =
      imgdata[i+2] =
      imgdata[i+3] = 0;
    }
  }
  ctx.putImageData(img, 0, 0);
})().catch(console.error);

But you can do this in a more performant way by using an Uint32Array view over the ImageData's .data to treat each full pixel as a single entry, a white pixel would be 0xFFFFFFFF (beware though if you want to target an other color, the order is now 0xAAGGBBRR).

(async() => {
  const resp = await fetch("https://i.ibb.co/ZKnmnT6/image.png");
  if (!resp.ok) {
    throw resp.status;
  }
  const blob = await resp.blob();
  const image = await createImageBitmap(blob);
  const canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(image, 0, 0);
  document.body.append(canvas);
  const img = ctx.getImageData(0, 0, image.width, image.height);
  const imgdata = new Uint32Array(img.data.buffer);
  for (let i = 0; i<imgdata.length; i++) {
    if (imgdata[i] !== 0xFFFFFFFF) {
      imgdata[i] = 0x00000000;
    }
  }
  ctx.putImageData(img, 0, 0);
})().catch(console.error);
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • This works!!. Thank you for the clarification. The second solution looks clean than the first one since the readability is better while trying to understand the code. – engageub Dec 28 '21 at 22:18
  • The above code has a similar problem since Alpha values can vary between 0 to 255 for white color and it represents opacity. Only three channels are required to check for white. Alpha channel represents opacity or transparency. It works here but not for all the cases. Using Uint32Array, it checks all the 4 values but I only need to consider RGB values for white since (255, 255, 255, 125) and (255, 255, 255, 255) are white color with different opacity. Here is the color code chart. https://www.december.com/html/spec/colorrgbadec.html – engageub Jan 08 '22 at 09:00