Remove color from image

I have a digital camera that takes pictures 24x7 and often depending on the weather returns images with an unpleasant color cast. Usually blue.

enter image description here

I am trying to find the source code or library that I can call from C # to reduce the color image.

Photoshop has a feature that works well on the images I tested:

  • Open image
  • Select Image → Correction → Match Color
  • Check the box "Neutralization"

This works well, but I don't know what he is doing.

enter image description here

I am not good at math, so I was looking for ideas about existing code or libraries that I could use.

I searched the Internet, but did not find anything useful - I would like to help.

+6
source share
5 answers

It appears that the white balance is set indoors (reddish light is expected), but in daylight (blue). GIMP has a color temperature slider that will change the composition of images. You are talking about preventing this from happening in the future or batch processing heaps of existing images. Even simple cameras (but maybe not mobile phones) have white balance controls for future shots.

Does this look like a webcam connected to your computer? So this is probably a moving target, meaning that the WB is reviewed every time it takes a pic, and you cannot apply the same correction to each image.

Here is an imagemagick script that can handle color temperature per bunch of images. I think that an approach that uses temperature will be better than one that simply normalizes levels, because if you shoot at the sky or the ocean, and it should be blue? You just want to make sure it is blue.

Edit: for a specific C # code, you can check here . The lower left example in the first set of balanced image colors is very similar to the original image. There is also a white balance function in the source code of paint.net

+2
source

Setting the white balance automatically may not always work well, because your algorithm will have less information as input (there is no real light measurement, only the pixel values ​​recorded by the matrix , some of which can be cropped ). Thus, this can help when the camera setup is ridiculously incorrect (for example, in your picture), but it cannot make WB correct. You better buy a decent camera (there are even cheap ones that can still take good pictures)

By the way, if you want to invent a wheel, the idea is to scale the color channels to make their average level equal. Here you can try different definitions of "medium", and you can also try to exclude pixels with cropped values ​​from the dimension. But there is no pleasure doing it again, as there are good ways to do it mentioned in @mickro's answer.

+4
source

I think the best solution is playing with ImageMagick , which gets .Net .

To run this thread fooobar.com/questions/318238 / ...

You should also find the right effect. Of course, good, trying to combine and neutralize the effects. Incidentally, this autocolour script may help.

hope help. Good luck.

+2
source

You can use OpenCV to develop an algorithm that will suit your needs. When I tried to find a solution to the problem, I realized that the problem of "color balance" can be solved in different ways.

I want to show you how to encode a very simple algorithm that will not completely recreate the “perfect” picture you get with Photoshop, but something is better than the original. Then you can find this topic in openCV on google and try a different approach. To encode this, I use the new OpenCV NuGet package, which you can get here . Just add the binary from openCV to your output directory (debug folder) and you will run it!

Then here is the code:

public Form1() { InitializeComponent(); NamedWindow windowsOriginal = new NamedWindow("Original"); NamedWindow windowsModified = new NamedWindow("Modified"); IplImage img = OpenCV.Net.CV.LoadImage(@"D:\hZpWG.jpg", LoadImageFlags.Color); IplImage imgDest = equalizeIntensity(img); windowsOriginal.ShowImage(img); windowsModified.ShowImage(imgDest); } IplImage equalizeIntensity(IplImage inputImage) { if(inputImage.Channels >= 3) { IplImage ycrcb = new IplImage(inputImage.Size, inputImage.Depth, inputImage.Channels); OpenCV.Net.CV.CvtColor(inputImage, ycrcb, ColorConversion.Bgr2YCrCb); IplImage Y = new IplImage(ycrcb.Size, IplDepth.U8, 1); IplImage Cr = new IplImage(ycrcb.Size, IplDepth.U8, 1); IplImage Cb = new IplImage(ycrcb.Size, IplDepth.U8, 1); OpenCV.Net.CV.Split(ycrcb, Y, Cr, Cb, null); OpenCV.Net.CV.EqualizeHist(Y, Y); IplImage result = new IplImage(inputImage.Size, IplDepth.U8, inputImage.Channels); OpenCV.Net.CV.Merge(Y, Cr, Cb, null, ycrcb); OpenCV.Net.CV.CvtColor(ycrcb, result, ColorConversion.YCrCb2Bgr); return result; } return null; } 

I put it in a form, but you can also use it in a console application.

Here is the result Originalenter image description here

Hope this helps!

+1
source

Create a histogram, auto-generate corrected levels (max, min. And gamma), apply levels to the image. Assuming you somehow collected your pixel data into an array of type Color ...

 public static Color[] AutoLevel(Color[] input) { var histogram = new Histogram(); foreach(var _ in input) histogram.Add(_); var levels = histogram.GetAutoLevels(); var ret = new Color[input.Length]; for(int _ = 0; _ < input.Length; _++) { ret[_] = levels.Apply(input[_]).ToColor(); } return ret; } 

... and here is the class ...

 public class Histogram { private long[,] _values = new long[3, 256]; public void AddColor(Color color) { AddColor(color.R, color.G, color.B); } public void AddColor(RGB color) { AddColor(color.R, color.G, color.B); } public void AddColor(byte r, byte g, byte b) { _values[0, b]++; _values[1, g]++; _values[2, b]++; } public long this[int channel, int index] { get { return _values[channel, index]; } } public long GetMaxValue() { var ret = long.MinValue; foreach(var _ in _values) if(_ > ret) ret = _; return ret; } public RGB GetMeanColor() { var total = new long[3]; var count = new long[3]; var value = new byte[3]; for(var _ = 0; _ < 3; _++) { for(var __ = 0; __ < 256; __++) { total[_] += (_values[_, __] * __); count[_] += _values[_, __]; } value[_] = (byte)Math.Round((double)total[_] / count[_]); } return new RGB(value[2], value[1], value[0]); } public RGB GetPercentileColor(double percentile) { var ret = new RGB(); for(var _ = 0; _ < 3; _++) { var total = 0L; for(var __ = 0; __ < 256; __++) total += _values[_, __]; var cutoff = (total * percentile); var count = 0L; for(var __ = 0; __ < 256; __++) { count += _values[_, __]; if(count > cutoff) { ret[_] = (byte)__; break; } } } return ret; } public Levels GetAutoLevels() { var low = GetPercentileColor(0.005); var middle = GetMeanColor(); var high = GetPercentileColor(0.995); return Levels.GetAdjusted(low, middle, high); } public class Levels { private RGB _inputLow = new RGB(0, 0, 0); private RGB _inputHigh = new RGB(255, 255, 255); private RGB _outputLow = new RGB(0, 0, 0); private RGB _outputHigh = new RGB(255, 255, 255); private double[] _gamma = { 1, 1, 1 }; public RGB InputLow { get { return _inputLow; } set { for(var _ = 0; _ < 3; _++) { if(value[_] == 255) value[_] = 254; if(_inputHigh[_] <= value[_]) _inputHigh[_] = (byte)(value[_] + 1); } _inputLow = value; } } public RGB InputHigh { get { return _inputHigh; } set { for(var _ = 0; _ < 3; _++) { if(value[_] == 0) value[_] = 1; if(_inputLow[_] >= value[_]) _inputLow[_] = (byte)(value[_] - 1); } _inputHigh = value; } } public RGB OutputLow { get { return _outputLow; } set { for(var _ = 0; _ < 3; _++) { if(value[_] == 255) value[_] = 254; if(_outputHigh[_] <= value[_]) _outputHigh[_] = (byte)(value[_] + 1); } _outputLow = value; } } public RGB OutputHigh { get { return _outputHigh; } set { for(var _ = 0; _ < 3; _++) { if(value[_] == 0) value[_] = 1; if(_outputLow[_] >= value[_]) _outputLow[_] = (byte)(value[_] - 1); } _outputHigh = value; } } public double GetGamma(int channel) { return _gamma[channel]; } public void SetGamma(int channel, double value) { _gamma[channel] = SetRange(value, 0.1, 10); } public RGB Apply(int r, int g, int b) { var ret = new RGB(); var input = new double[] { b, g, r }; for(var _ = 0; _ < 3; _++) { var value_ = (input[_] - _inputLow[_]); if(value_ < 0) { ret[_] = _outputLow[_]; } else if((_inputLow[_] + value_) >= _inputHigh[_]) { ret[_] = _outputHigh[_]; } else { ret[_] = (byte)SetRange((_outputLow[_] + ((_outputHigh[_] - _outputLow[_]) * Math.Pow((value_ / (_inputHigh[_] - _inputLow[_])), _gamma[_]))), 0, 255); } } return ret; } internal static Levels GetAdjusted(RGB low, RGB middle, RGB high) { var ret = new Levels(); for(var _ = 0; _ < 3; _++) { if((low[_] < middle[_]) && (middle[_] < high[_])) { ret._gamma[_] = SetRange(Math.Log(0.5, ((double)(middle[_] - low[_]) / (high[_] - low[_]))), 0.1, 10); } else { ret._gamma[_] = 1; } } ret._inputLow = low; ret._inputHigh = high; return ret; } } private static double SetRange(double value, double min, double max) { if(value < min) value = min; if(value > max) value = max; return value; } public struct RGB { public byte B; public byte G; public byte R; public RGB(byte r, byte g, byte b) { B = b; G = g; R = r; } public byte this[int channel] { get { switch(channel) { case 0: return B; case 1: return G; case 2: return R; default: throw new ArgumentOutOfRangeException(); } } set { switch(channel) { case 0: B = value; break; case 1: G = value; break; case 2: R = value; break; default: throw new ArgumentOutOfRangeException(); } } } public Color ToColor() { return Color.FromArgb(R, G, B); } } } 

Results:
results

0
source

Source: https://habr.com/ru/post/953091/


All Articles