skip to Main Content

This affect here (image below) was achieved with a couple simple Photoshop steps takes, the colors parts were turn white, the background (various shades of white gray), was made transparent. Is it possible to achieve this with canvas?

The images inside the circles below is the final result.

The images were originally colored, like the 2nd from top image was this one:

See that circle in the middle, basically all the white was cut out in an aliased way.

Same with this zoho logo:

The 2nd from bottom was originally something like this:

Except the red R was just a Y in the middle and instead of all the text and green strip seen in image here, it just had some grainy texture in shades of gray around it. And via photoshop the Y was made trasnparent, and the texture and stamp was just made solid, removing the 3d shadow etc.

Putting this above yandex stamp through the photoshop algorithm gives this (i replaced the white with black for demo/visibility puproses)

This was jagged after the photoshop algorithm but in final application the image is reduced to around 80x80px and that makes it look real smooth and anti-aliased. So real final result is this which looks very decent.

2

Answers


  1. The problem is multifaceted as there are regions which require different approaches, for example, the last image where the main text needs to be converted to white but keep transparency, while the bottom bar in the same image is solid but need the white text to be retained while the solid background to be removed.

    It’s doable by implementing tools to select regions and apply various operators manually – automatically will be a much larger challenge than it may appear to be.

    You could make requirements to the user to only upload images with an alpha channel. For that you can simply replace each non-transparent pixel with white. It becomes more a policy issue than a technical one in my opinion.

    For example

    Taking the logo:

    logo

    var img = new Image();
    img.crossOrigin = "";
    img.onload = process;
    img.src = "http://i.imgur.com/HIhnb4A.png";        // load the logo
    
    function process() {
      var canvas = document.querySelector("canvas"),   // canvas
          ctx = canvas.getContext("2d"),               // context
          w = this.width,                              // image width/height
          h = this.height,
          idata, data32, len, i, px;                   // iterator, pixel etc.
      
      canvas.width = w;                                // set canvas size
      canvas.height = h;
      ctx.drawImage(this, 0, 0);                       // draw in image
      
      idata = ctx.getImageData(0, 0, w, h);            // get imagedata
      data32 = new Uint32Array(idata.data.buffer);     // use uint32 view for speed
      len = data32.length;
      
      for(i = 0; i < len; i++) {
        // extract alpha channel from a pixel
        px = data32[i] & 0xff000000;                   // little-endian: ABGR
        
        // any non-transparency? ie. alpha > 0
        if (px) {
          data32[i] = px | 0xffffff;    // set this pixel to white, keep alpha level
        }
      }
      
      // done
      ctx.putImageData(idata, 0, 0);
    }
    body {background:gold}
    <canvas></canvas>

    Now the problem is easy to spot: the "@" character is just solid because there is no transparency behind it. To automate this would require first to knock out all whites, then apply the process demoed above. However, this may work in this single case but probably not be a good thing for most.

    There will also be anti-aliasing issues as it’s not possible to know how much of the white you want to knock out as we don’t analyze the edges around the white pixels. Another possible challenge is ICC corrected image where white may not be white depending on ICC profile used, browser support and so forth.

    But, it’s doable to some degree – taking the code above with a prestep to knock out entirely white pixels for this logo:

    var img = new Image();
    img.crossOrigin = "";
    img.onload = process;
    img.src = "http://i.imgur.com/HIhnb4A.png";        // load the logo
    function process() {
      var canvas = document.querySelector("canvas"),   // canvas
          ctx = canvas.getContext("2d"),               // context
          w = this.width, h = this.height,
          idata, data32, len, i, px;                   // iterator, pixel etc.
      canvas.width = w;                                // set canvas size
      canvas.height = h;
      ctx.drawImage(this, 0, 0);                       // draw in image
      
      idata = ctx.getImageData(0, 0, w, h);            // get imagedata
      data32 = new Uint32Array(idata.data.buffer);     // use uint32 view for speed
      len = data32.length;
      
      for(i = 0; i < len; i++) {
        px = data32[i];                                // pixel
    
        // is white? then knock it out
        if (px === 0xffffffff) data32[i] = px = 0;
        
        // extract alpha channel from a pixel
        px = px & 0xff000000;                          // little-endian: ABGR
        
        // any non-transparency? ie. alpha > 0
        if (px) {
          data32[i] = px | 0xffffff;    // set this pixel to white, keep alpha level
        }
      }
      
      ctx.putImageData(idata, 0, 0);
    }
    body {background:gold}
    <canvas></canvas>
    Login or Signup to reply.
  2. Use this

    private draw(base64: string) {
      // example size
      const width = 200;
      const height = 70;
      const image = new Image();
      image.onload = () => {
        const canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0);
        const imageData = ctx.getImageData(0, 0, width, height);
        for (let x = 0; x < imageData.width; x++) {
          for (let y = 0; y < imageData.height; y++) {
            const offset = (y * imageData.width + x) * 4;
            const r = imageData.data[offset];
            const g = imageData.data[offset + 1];
            const b = imageData.data[offset + 2];
            // if it is pure white, change its alpha to 0
            if (r == 255 && g == 255 && b == 255) {
              imageData.data[offset + 3] = 0;
            }
          }
        }
        ctx.putImageData(imageData, 0, 0);
        // output base64
        const result = canvas.toDataURL();
      };
      image.src = base64;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search