1

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.

7
  • 1
    Short answer is yes you can achieve this with canvas, and I am curious as to see how to go about it. Commented May 25, 2015 at 12:40
  • 2
    If you do a search there should be plenty of tutorials for greyscaling in a canvas, which is basically what this is with a little something extra for the background etc. Commented May 25, 2015 at 12:42
  • Thanks guys. Thanks @adeneo its not a purely grayscale though, as seen in the yandex logo, 2nd from bottom. There was many details in that, it originally looked like this: i.imgur.com/iHJWaHL.png except the R was a Y and in the middle, so it took out all the shading 3d'ness and made the Y transparent and the stamp borderless. Commented May 25, 2015 at 12:52
  • 2
    Interesting for sure but not sure why this question is being upvoted despite showing no own research or coding attempts, making it much too broad and hard to answer. Commented May 25, 2015 at 13:00
  • 1
    This is a job suitable for Photoshop. The images are too special cases on their own to use a generic approach, f.ex. different regions within the same image require different approached and some regions are limited to a char. Theoretically possible to do in canvas just like a bitmap in Photoshop, but PS already have all the tools you need. Suggestion: prep a flat colored image with alpha channel in PS, this can be used to place on a colored bg, and if needed would be a good basis to easily change the flat color dynamically using canvas and comp modes. Commented May 25, 2015 at 15:27

2 Answers 2

3

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>

Sign up to request clarification or add additional context in comments.

3 Comments

This is super cool thank you! Its a good a start as any. The anti aliasing im doing a silly thing where i reduce the size of the image :)
@Noitidart I forgot to mention that you can scale up the image (x2-4) before processing it, process and then scale back down to original size. This is very similar to what you're already do, but can be handy way to add some anti-aliasing with images that needs to stay the same size as when uploaded.
Oh wow thats a cool trick I didn't know you can scale up and then down to acheive trick antialiasing! Thank you! :)
1

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; } 

1 Comment

Thanks! I'll try it out!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.