Change background colour of an anti-aliased image

2019-05-14 02:41发布

问题:

I have an image where I need to change the background colour (E.g. changing the background of the example image below to blue).

However, the image is anti-aliased so I cannot simply do a replace of the background colour with a different colour.

One way I have tried is creating a second image that is just the background and changing the colour of that and merging the two images into one, however this does not work as the border between the two images is fuzzy.

Is there any way to do this, or some other way to achieve this that I have no considered?

Example image

回答1:

Abstractly

Each pixel in your image is a (R, G, B) vector, where each component is in the range [0, 1]. You want a transform, T, that will convert all of the pixels in your image to a new (R', G', B') under the following constraints:

  • black should stay black
    • T(0, 0, 0) = (0, 0, 0)
  • white should become your chosen color C*
    • T(1, 1, 1) = C*

A straightforward way to do this is to choose the following transform T:

T(c) = C* .* c  (where .* denotes element-wise multiplication)

This is just standard image multiplication.

Concretely

If you're not worried about performance, you can use the (very slow) methods GetPixel and SetPixel on your Bitmap to apply this transform for each pixel in it. If it's not clear how to do this, just say so in a comment and I'll add a detailed explanation for that part.

Comparison

Compare this to the method presented by LarsTech. The method presented here is on the top; the method presented by LarsTech is on the bottom. Notice the undesirable edge effects on the bottom icon (white haze on the edges).

And here is the image difference of the two:

Afterthought

If your source image has a transparent (i.e. transparent-white) background and black foreground (as in your example), then you can simply make your transform T(a, r, g, b) = (a, 0, 0, 0) then draw your image on top of whatever background color you want, as LarsTech suggested.



回答2:

Just using GDI+

Image image = Image.FromFile("cloud.png");

Bitmap bmp = new Bitmap(image.Width, image.Height);
using (Graphics g = Graphics.FromImage(bmp)) {
  g.Clear(Color.SkyBlue);
  g.InterpolationMode = InterpolationMode.NearestNeighbor;
  g.PixelOffsetMode = PixelOffsetMode.None;
  g.DrawImage(image, Point.Empty);
}

resulted in:



回答3:

If it is a uniform colour you want to replace you could convert this to an alpha. I wouldn't like to code it myself!

You could use GIMP's Color To Alpha source code (It's GPL), here's a version of it

P.S. Not sure how to get the latest.



回答4:

Background removal /replacement, IMO is more art than science, you’ll not find one algorithm fit all solution for this BUT depending on how desperate or interested you are in solving this problem, you may want to consider the following explanation:

Let’s assume you have a color image.

Use your choice of decoding mechanism and generate a gray scale / luminosity image of your color image.

Plot a graph (metaphorically speaking) of numeric value of the pixel(x) vs number of pixels in the image for that value(y). Aka. a luminosity histogram.

Now if your background is large enough (or small), you’d see a part of the graph representing the distribution of a range of pixels which constitute your background. You may want to select a slightly wider range to handle the anti-aliasing (based on a fixed offset that you define if you are dealing with similar images) and call it the luminosity range for your background.

It would make your life easier if you know at least one pixel (sample/median pixel value) out of the range of pixels which defines your background, that way you can ‘look up’ the part of the graph which defines your background.

Once you have the range of luminosity pixels for the background, you may run through the original image pixels, compare their luminosity values with the range you have, if it falls within, replace the pixel in the original image with the desired color, preferably luminosity shifted based on the original pixel and the sample pixel, so that the replaced background looks anti-aliased too.

This is not a perfect solution and there are a lot of scenarios where it might fail / partially fail, but again it would work for the sample image that you had attached with your question.

Also there are a lot of performance improvement opportunities, including GPGPU etc.

Another possible solution would be to use some of the pre-built third party image processing libraries, there are a few open source such as Camellia but I am not sure of what features are provided and how sophisticated they are.