Replacing colour of an Image - c#

I am trying to replace the black colours of a picture with white, and vice versa. This is actually so my OCR code can read it on white backgrounds better. It's currently getting the image from clipboard
Image img = Clipboard.GetImage();
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = img;
I've seen some other questions where they're working with an actual bitmap, but how do I approach it direct from Clipboard?

Another solution using the ColorMatrix class.
You can use the Graphics.DrawImage overload that accepts an ImageAttributes argument.
ImageAttributes.SetColorMatrix() sets the color-adjustment matrix, optionally targeting a specific category (Bitmap, Pen, Brush etc.) and can be instructed to skip the Gray Colors, modify the Gray colors only or all Colors.
The ImageAttributes.SetThreshold() method allows to regulate the Colors cutoff point (threshold) to fine tune the Brightness.
It accepts values from 0 to 1.
When set to 0, an image is all white, all black when set to 1 (see the Docs about it).
Also consider that the "Inversion" depends on the original bitmap color pattern, so try different approaches. Sometimes, inverting the brightness can give you a better result, sometime it doesn't.
You OCR must be "trained", to verify what values suits it better.
Take a look at these articles:
Recoloring (MSDN)
ASCII Art Generator (CodeProject)
Brightness Matrix:
R=Red G=Green B=Blue A=Alpha Channel W=White (Brightness)
Modify the Brightness component to obtain an "Inversion"
R G B A W
R [1 0 0 0 0]
G [0 1 0 0 0]
B [0 0 1 0 0]
A [0 0 0 1 0]
W [b b b 0 1] <= Brightness
using System.Drawing;
using System.Drawing.Imaging;
// ...
Image colorImage = Clipboard.GetImage();
// Default values, no inversion, no threshold adjustment
var bmpBlackWhite = BitmapToBlackAndWhite(colorImage);
// Inverted, use threshold adjustment set to .75f
var bmpBlackWhite = BitmapToBlackAndWhite(colorImage, true, true, .75f);
// ...
private Bitmap BitmapToBlackAndWhite(Image image, bool invert = false, bool useThreshold = false, float threshold = .5f)
{
var mxBlackWhiteInverted = new float[][]
{
new float[] { -1, -1, -1, 0, 0},
new float[] { -1, -1, -1, 0, 0},
new float[] { -1, -1, -1, 0, 0},
new float[] { 0, 0, 0, 1, 0},
new float[] { 1, 1, 1, 0, 1}
};
var mxBlackWhite = new float[][]
{
new float[] { 1, 1, 1, 0, 0},
new float[] { 1, 1, 1, 0, 0},
new float[] { 1, 1, 1, 0, 0},
new float[] { 0, 0, 0, 1, 0},
new float[] {-1, -1, -1, 0, 1}
};
var bitmap = new Bitmap(image.Width, image.Height);
using (var g = Graphics.FromImage(bitmap))
using (var attributes = new ImageAttributes()) {
attributes.SetColorMatrix(new ColorMatrix(invert ? mxBlackWhiteInverted : mxBlackWhite));
// Adjust the threshold as needed
if (useThreshold) attributes.SetThreshold(threshold);
var rect = new Rectangle(Point.Empty, image.Size);
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
return bitmap;
}
}

You can use the ColorMap and ImageAttributes classes from the System.Drawimg.Imaging namespace to directly replace the pixels in your image:
Image img = Clipboard.GetImage();
if (img != null) {
ColorMap[] cm = new ColorMap[1];
cm[0] = new ColorMap();
cm[0].OldColor = Color.Black;
cm[0].NewColor = Color.White;
ImageAttributes ia = new ImageAttributes();
ia.SetRemapTable(cm);
using (Graphics g = Graphics.FromImage(img)) {
g.DrawImage(img, new Rectangle(Point.Empty, img.Size), 0, 0, img.Width, img.Height,
GraphicsUnit.Pixel, ia);
}
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = img;
}

if your image is B&W, it is very easy in OpenCV:
// bmp is your bitmap
var inverted = (255 - bitmap.ToMat()).ToMat().
.ToBitamp() // or
.ToWriteableBitmap() // or
.ToBitmapSource()
OpenCV can be a little overkill if this in your only manipulation in the whole app

Related

How to set the color of a metafile to a specific value?

I need to change the color of all objects available in a metafile into an specific color. However, what is currently provided is just for translating/scaling/rotating colors. For example I need to set the color of all available objects to green.
What is currently discussed below is to just change the colors relatively, not absolutely:
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/recoloring-images
How to change colors of EMF+ image in C#
What this code can do is just translate all the colors to white color. But what I need is to set all the colors to e.g. green, dark red, and so on. Is there any way to do this?
ImageAttributes GetAttr()
{
/* Get image attributes to translate to white */
float[][] colorMatrixElements = {
new float[] {1, 0, 0, 0, 0}, // no red scaling
new float[] {0, 1, 0, 0, 0}, // no green scaling
new float[] {0, 0, 1, 0, 0}, // no blue scaling
new float[] {0, 0, 0, 1, 0}, // no alpha scaling
new float[] {1, 1, 1, 1, 1}}; // four translations of 1.0
ImageAttributes imageAttributes = new ImageAttributes();
imageAttributes.SetColorMatrix(new ColorMatrix(colorMatrixElements));
return imageAttributes;
}
void test()
{
var fname = Path.GetTempFileName();
var frame = new Rectangle(0, 0, 640, 480);
using (var img = new Metafile(fname, CreateGraphics().GetHdc(), frame, MetafileFrameUnit.Point))
{
using (var g = Graphics.FromImage(img))
{
var arrow = new Metafile("arrow.emf");
g.DrawImage(arrow, new Rectangle(150, 400, 75, 50),
0, 0, arrow.Width, arrow.Height, GraphicsUnit.Pixel, GetAttr());
}
}
}

Difference between transforming an image with a Color Matrix or by Bitmap.SetPixel

I have the following codes that transform an image's pixels:
Transforming using ColorMatrix
Bitmap bmpOut = null;
if (DisplayImage == null)
{
return;
}
bmpOut = GetBitmapFromImageSoure(OriginalImage.Source);
Bitmap originalImage = bmpOut;
Bitmap adjustedImage = new Bitmap(bmpOut.Width, bmpOut.Height);
float brightness = (float)BrightnessSlider.Value;
float contrast = (float)ContrastSlider.Value;
float gamma = (float)GammaSlider.Value;
float alpha = (float)AlphaSlider.Value;
float adjustedBrightness = brightness - 1.0f;
// create matrix that will brighten and contrast the image
float[][] ptsArray ={
new float[] {contrast, 0, 0, 0, 0}, // scale red
new float[] {0, contrast, 0, 0, 0}, // scale green
new float[] {0, 0, contrast, 0, 0}, // scale blue
new float[] {0, 0, 0, alpha, 0}, // scale alpha
new float[] {adjustedBrightness, adjustedBrightness, adjustedBrightness, 0, 1}}; // brightness
ImageAttributes imageAttributes = new ImageAttributes();
imageAttributes.ClearColorMatrix();
imageAttributes.SetColorMatrix(new ColorMatrix(ptsArray), ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
imageAttributes.SetGamma(gamma, ColorAdjustType.Bitmap);
Graphics g = Graphics.FromImage(adjustedImage);
g.DrawImage(originalImage, new System.Drawing.Rectangle(0, 0, adjustedImage.Width, adjustedImage.Height)
, 0, 0, originalImage.Width, originalImage.Height,
GraphicsUnit.Pixel, imageAttributes);
this.DisplayImage.Source = ConvertToBitmapImage(adjustedImage);
Changing each pixel to transform to gray scale
Bitmap original = GetBitmapFromImageSoure(OriginalImage.Source);
Bitmap converted = (Bitmap)original.Clone();
System.Drawing.Color c;
for (int i = 0; i < converted.Width; i++)
{
for (int j = 0; j < converted.Height; j++)
{
c = converted.GetPixel(i, j);
byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
converted.SetPixel(i, j, System.Drawing.Color.FromArgb(gray, gray, gray));
}
}
DisplayImage.Source = ConvertToBitmapImage(converted);
The first method is for changing the brightness, gamma, contrast of an image which is controlled by a slider and immidiatly transform the image.
While the second method takes time to proccess the same image with just a checkbox.
Why is using a color matrix faster and how does it work better than changing each pixel in a loop.

C# Color an image with shadows

I am currently creating sort of a game with C# and am trying to create outfits for the players. I would like to make cloth design and let players chose the colors.
I took pictures from gamefiles of TibiaME (tibiame.com), which does pretty much what i want to to.
How can I Fill this form with color? When I try to replace a certain color, it does not work, since it's not the same everyhwere. The shadows look pretty cool :P
The simplest (and fastest) way to color (tint) an image is to use a ColorMatrix.
Here is the result of using nine colors to tint the original:
Note that I have photoshopped the posted image bo be transparent around the center part; using just the original looks like this..:
((The glitch in the lower right is in the original..))
Here is a function the returns a list of tinted version of an image, one for each color in a list..:
List<Bitmap> TintImages(Bitmap bmp0, List<Color> colors )
{
List<Bitmap> tinted = new List<Bitmap>();
Size sz = bmp0.Size;
float f = 256f;
for (int i = 0; i < colors.Count; i++)
{
float r = colors[i].R / f;
float g = colors[i].G / f;
float b = colors[i].B / f;
float[][] colorMatrixElements = {
new float[] {r, 0, 0, 0, 0}, // red scaling factor of
new float[] {0, g, 0, 0, 0}, // green scaling factor
new float[] {0, 0, b, 0, 0}, // blue scaling factor
new float[] {0, 0, 0, 1, 0}, // alpha scaling factor
new float[] {0, 0, 0, 0, 1}}; // no further translations
ImageAttributes imageAttributes = new ImageAttributes();
ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);
imageAttributes.SetColorMatrix(
colorMatrix,
ColorMatrixFlag.Default,
ColorAdjustType.Bitmap);
Bitmap bmp = new Bitmap(sz.Width, sz.Height);
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.DrawImage(bmp0, new Rectangle(0, 0, sz.Width, sz.Height),
0, 0, sz.Width, sz.Height, GraphicsUnit.Pixel, imageAttributes);
tinted.Add(bmp);
}
}
return tinted;
}
You could iterate over each pixel of the bitmap and make a color shift in the direction you want. When I say colorshoft I mean a you have to adapt the RGB values of each pixel.
A simple shift to red could look like this:
for (int Xcount = 0; Xcount < myBitmap.Width; Xcount++)
{
for (int Ycount = 0; Ycount < myBitmap.Height; Ycount++)
{
//get color of the pixel
Color pixelColor = myBitmap.GetPixel(Xcount, Ycount);
byte red = pixelColor.R;
byte green = pixelColor.G;
byte blue = pixelColor.B;
//make shift and prevent overflow
if (red < 205)
red += 50;
else
red = 255;
//set color of the pixel
myBitmap.SetPixel(Xcount, Ycount, Color.FromRgb(red, green, blue));
}
}
Keep in mind this is just a simple example and may not result in what you expected.
You can read more about the RGB colorspace here: RGB color model and here you find a RGB Color Codes Chart

How to change colors of EMF+ image in C#

I'm trying to develop a special kind of heatmap, where the color of a marker depends on the value of some calculated variables.
What I need to do is change the color of my existing EMF+-Image. The following code works like a charm when using png or wmf files, but DrawImage doesn't draw anything when using an EMF+ file:
//EMF+ image (color = red)
Metafile mf = new Metafile(#"C:\output\redman.emf");
//changes the color of the image from red to green (works with .png, but not with EMF+)
float[][] matrixColTrans =
{
new float[] {215.0f / 195.0f, 0, 0, 0, 0}
, new float[] {0, 240.0f / 45.0f, 0, 0, 0}
, new float[] {0, 0, 80.0f / 5.0f, 0, 0}
, new float[] {0, 0, 0, 1, 0}
, new float[] {0, 0, 0, 0, 1}
};
ColorMatrix colorMatrix = new ColorMatrix(matrixColTrans);
ImageAttributes ia = new ImageAttributes();
ia.SetColorMatrix(colorMatrix);
g.DrawImage(
mf
, new Rectangle(80, 0, 20, 50)// destination rectangle
, 0, 0 // upper-left corner of source rectangle
, mf.Width // width of source rectangle
, mf.Height // height of source rectangle
, GraphicsUnit.Pixel
, ia
);
Same thing happens when using ia.SetRemapTable for example.
Any ideas on how to solve that problem?

How to do color balancing using a gray card in C#

I need to color balance an image that has an 18% gray card in it. The user loads this image into the application, then clicks on the gray card. From here is where I need help with an algorithm to color balance the image. I've found a few articles that mention doing a matrix transform, which I've tried, but without success (the image washes out or turns one color or another). The code I have now is:
int sampleSize = 20; // The square around the user's click on the gray card
int rVal = 0, gVal = 0, bVal = 0;
int count = 0;
for (int x = 0; x < sampleSize - 1; x++)
{
for (int y = 0; y < sampleSize - 1; y++)
{
System.Drawing.Color c = grayCardArea.GetPixel(x, y);
if (c.R > 0)
{
rVal += c.R;
gVal += c.G;
bVal += c.B;
rs.Add(c.R);
count++;
}
}
}
grayCardGraphics.Dispose();
int rAvg = 0, gAvg = 0, bAvg = 0;
rAvg = (int)Math.Round((decimal)rVal / (count));
gAvg = (int)Math.Round((decimal)gVal / (count));
bAvg = (int)Math.Round((decimal)bVal / (count));
// 117 is a value I found online for the neutral gray color of the gray card
float rDiff = (117 / (float)rAvg);
float gDiff = (117 / (float)gAvg);
float bDiff = (117 / (float)bAvg);
float[][] ptsArray =
{
new float[] {rDiff, 0, 0, 0, 0},
new float[] {0, gDiff, 0, 0, 0},
new float[] {0, 0, bDiff, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, .0f, 1}
};
// Create a ColorMatrix
ColorMatrix clrMatrix = new ColorMatrix(ptsArray);
// Create ImageAttributes
ImageAttributes imgAttribs = new ImageAttributes();
// Set color matrix
imgAttribs.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default);
// Draw image with ImageAttributes
outputImageGraphics.DrawImage(srcImage, new System.Drawing.Rectangle(0, 0, srcImage.Width, srcImage.Height),
0, 0, srcImage.Width, srcImage.Height,
GraphicsUnit.Pixel, imgAttribs);
Viewing a saved copy of the outputImage shows an odd transformation of the image.
Any help is greatly appreciated!
My company, Atalasoft, has a free .NET Imaging SDK, with a class called LevelsCommand, that I think will do what you want.
http://atalasoft.com/photofree
Code is something like
AtalaImage img = new AtalaImage("filename");
LevelsCommand cmd = new LevelsCommand(/* ... */ ); // need to pass in leveling colors
img = cmd.Apply(img).Image;
img.Save("filename-new", new PngEncoder(), null); // or could be new JpegEncoder() or something else
You should use proper extensions on filenames to indicate the format.
Your first assumption appears to be that the image was properly exposed in the first place and that making the gray card read 117, 117, 117 will solve the problem. My advice is to leave the exposure alone and adjust just the color cast. You might find a different color model useful -- e.g., HSL. The saturation of a gray card should always be zero.
Alternatively, I have an example gray target reading 71, 72, 60. This is a bit warm. It would stand to reason that a more correct reading would be 67,67,67 or (R+G+B)/3. Because the image is a bit underexposed, I left it that way, but achieved a true neutral without altering the density of the image.
I hope this provides some help along your path toward getting the color right.

Categories