How to convert mobile image into Black and white C# - c#

I have to convert image into black and white, which is captured by using Mobile camera.
I had read questions and answers related to converting image into black and white, but provided solution won't help me.
Below are my image which I had captured.
So I have to save above image in my application folder by converting it into black and white as per of requirement.
I had tried below c# codes but it gives me incomplete image.
Code 1
Bitmap bmp = new Bitmap(#"c:\test.jpg");
Bitmap bw = bmp.Clone(new Rectangle(0, 0, bmp.Width, bmp.Height),
PixelFormat.Format8bppIndexed);
Code 2
Bitmap bmp = new Bitmap(#"c:\test.jpg");
int width = bmp.Width;
int height = bmp.Height;
int[] arr = new int[225];
int i = 0;
Color p;
//Grayscale
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
p = bmp.GetPixel(x, y);
int a = p.A;
int r = p.R;
int g = p.G;
int b = p.B;
int avg = (r + g + b) / 3;
avg = avg < 128 ? 0 : 255; // Converting gray pixels to either pure black or pure white
bmp.SetPixel(x, y, Color.FromArgb(a, avg, avg, avg));
}
}
But both code are converting original image looks like below.
It's may be due to shadowing while capturing image using mobile.
Pls let me know how can I convert this image into black and white without lose of image.
Is there any library which help me anything.

Reduce the threshold to 90 here avg = avg < 90 ? 0 : 255;
or you can use EmguCV, it's much more faster and easier, and they both give the same result.
Image<Gray, byte> img = new Image<Gray, byte>("1.jpg");
img._ThresholdBinary(new Gray(90), new Gray(255));

Related

How to change color of a image in C#

I am developing a Windows application in which I am getting an image (in black color) from server. I download that image and display it in my application.
Is there a way to change the color of this image (to white color) in code because I want to display a white colored image since I have a black background.
Please let me know if additional info is required from my side?
Depending on how far you want to go down this rabbit hole.
You could just convert the image to 32bit and roll your own image processing routine to convert black to white pixels.
The following is an example of how to use unsafe keyword and Pointers to achieve this fairly efficiently. Add pepper and salt to taste
-unsafe (C# Compiler Options)
Disclaimer there is other ways to do this, however YOLO
unsafe private void ConvertImage(string fromPath, string toPath)
{
using (Bitmap orig = new Bitmap(fromPath))
{
using (Bitmap clone = new Bitmap(orig.Width, orig.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb))
{
var rect = new Rectangle(0, 0, clone.Width, clone.Height);
using (Graphics gr = Graphics.FromImage(clone))
{
gr.DrawImage(orig, rect);
}
// lock the array for direct access
var bitmapData = clone.LockBits(Bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
// get the pointer
var scan0Ptr = (int*)bitmapData.Scan0;
// get the stride
var stride = bitmapData.Stride / 4;
var white = Color.White.ToArgb();
var black = Color.Black.ToArgb();
// scan all x
for (var x = rect.Left; x < rect.Right; x++)
{
var pX = scan0Ptr + x;
// scan all y
for (var y = rect.Top; y < rect.Bottom; y++)
{
if (*(pX + y * stride) == black)
{
*(pX + y * stride) = white;
}
else
{
*(pX + y * stride) = black;
}
}
}
// unlock the bitmap
clone.UnlockBits(bitmapData);
clone.Save(toPath);
}
}
}
Updated
Changed to invert the image

Get grayscale Image height without considering white pixels

I have grayscale pictures of an ArrayList<System.Windows.Controls.Image> laid out horizontally on a Canvas. Their ImageSource are of type System.Windows.Media.Imaging.BitmapImage.
Is there a way to measure in pixels the height of each Image without considering white, non-transparent pixels Outside the colored part ?
Lets say I have an Image of height 10, in which the whole top half is white and the bottom half is black; I would need to get 5 as it's height. In the same way, if that Image had the top third black, middle third white and bottom third black, the height would be 10.
Here's a drawing that shows the desired heights (in blue) of 3 images:
I am willing to use another type for the images, but it Must be possible to either get from a byte[] array to that type, or to convert Image to it.
I have read the docs on Image, ImageSource and Visual, but I really have no clue where to start.
Accessing pixel data from a BitmapImage is a bit of a hassle, but you can construct a WriteableBitmap from the BitmapImage object which is much easier (not to mention more efficient).
WriteableBitmap bmp = new WriteableBitmap(img.Source as BitmapImage);
bmp.Lock();
unsafe
{
int width = bmp.PixelWidth;
int height = bmp.PixelHeight;
byte* ptr = (byte*)bmp.BackBuffer;
int stride = bmp.BackBufferStride;
int bpp = 4; // Assuming Bgra image format
int hms;
for (int y = 0; y < height; y++)
{
hms = y * stride;
for (int x = 0; x < width; x++)
{
int idx = hms + (x * bpp);
byte b = ptr[idx];
byte g = ptr[idx + 1];
byte r = ptr[idx + 2];
byte a = ptr[idx + 3];
// Construct your histogram
}
}
}
bmp.Unlock();
From here, you can construct a histogram from the pixel data, and analyze that histogram to find the boundaries of the non-white pixels in the images.
EDIT: Here's a Silverlight solution:
public static int getNonWhiteHeight(this Image img)
{
WriteableBitmap bmp = new WriteableBitmap(img.Source as BitmapImage);
int topWhiteRowCount = 0;
int width = bmp.PixelWidth;
int height = bmp.PixelHeight;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int pixel = bmp.Pixels[y * width + x];
if (pixel != -1)
{
topWhiteRowCount = y - 1;
goto returnLbl;
}
}
}
returnLbl:
return topWhiteRowCount >= 0 ? height - topWhiteRowCount : height;
}

How to convert from System.Drawing.Bitmap to grayscale, then to array of doubles (Double[,]), then back to grayscale Bitmap?

I need to perform some mathematical operations in photographs, and for that I need the floating point grayscale version of an image (which might come from JPG, PNG or BMP files with various colordepths).
I used to do that in Python using PIL and scipy.ndimage, and it was very straightforward to convert to grayscale with PIL and then to an array of floating-point numbers with numpy, but now I need to do something similar in C#, and I'm confused how to do so.
I have read this very nice tutorial, that seems to be a recurring reference, but that only covers the "convert to grayscale" part, I am not sure how to get an array of doubles from a Bitmap, and then (at some moment) to convert it back to System.Drawing.Bitmap for viewing.
I'm sure there are loads of optimal ways to do this.
As #Groo points out perfectly in the comments section, one could use for instance the LockBits method to write and read pixel colors to and from a Bitmap instance.
Going even further, one could use the graphics card of the computer to do the actual computations.
Furthermore, the method Color ToGrayscaleColor(Color color) which turns a color into its
grayscale version is not optically correct. There is a set of ratios which actually need to be applied to the color component strengths. I just used 1, 1, 1 ratios. That's accceptable for me and probably horrible for an artist or a scientist.
In the comments section, #plinth was very nice to point out to this question you should look at, if you want to make an anatomically correct conversion: Converting RGB to grayscale/intensity
Just wanted to share this really easy to understand and implement solution:
First a little helper to turn a Color into it's grayscale version:
public static Color ToGrayscaleColor(Color color) {
var level = (byte)((color.R + color.G + color.B) / 3);
var result = Color.FromArgb(level, level, level);
return result;
}
Then for the color bitmap to grayscale bitmap conversion:
public static Bitmap ToGrayscale(Bitmap bitmap) {
var result = new Bitmap(bitmap.Width, bitmap.Height);
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++) {
var grayColor = ToGrayscaleColor(bitmap.GetPixel(x, y));
result.SetPixel(x, y, grayColor);
}
return result;
}
The doubles part is quite easy. The Bitmap object is a memory representation of the actual image which you can use in various operations. The colordepth and image format details are only the concern of loading and saving instances of Bitmap onto streams or files. We needn't care about those at this point:
public static double[,] FromGrayscaleToDoubles(Bitmap bitmap) {
var result = new double[bitmap.Width, bitmap.Height];
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++)
result[x, y] = (double)bitmap.GetPixel(x, y).R / 255;
return result;
}
And turning a double array back into a grayscale image:
public static Bitmap FromDoublesToGrayscal(double[,] doubles) {
var result = new Bitmap(doubles.GetLength(0), doubles.GetLength(1));
for (int x = 0; x < result.Width; x++)
for (int y = 0; y < result.Height; y++) {
int level = (int)Math.Round(doubles[x, y] * 255);
if (level > 255) level = 255; // just to be sure
if (level < 0) level = 0; // just to be sure
result.SetPixel(x, y, Color.FromArgb(level, level, level));
}
return result;
}
The following lines:
if (level > 255) level = 255; // just to be sure
level < 0) level = 0; // just to be sure
are really there in case you operate on the doubles and you want to allow room for little mistakes.
The final code, based mostly in tips taken from the comments, specifically the LockBits part (blog post here) and the perceptual balancing between R, G and B values (not paramount here, but something to know about):
private double[,] TransformaImagemEmArray(System.Drawing.Bitmap imagem) {
// Transforma a imagem de entrada em um array de doubles
// com os valores grayscale da imagem
BitmapData bitmap_data = imagem.LockBits(new System.Drawing.Rectangle(0,0,_foto_franjas_original.Width,_foto_franjas_original.Height),
ImageLockMode.ReadOnly, _foto_franjas_original.PixelFormat);
int pixelsize = System.Drawing.Image.GetPixelFormatSize(bitmap_data.PixelFormat)/8;
IntPtr pointer = bitmap_data.Scan0;
int nbytes = bitmap_data.Height * bitmap_data.Stride;
byte[] imagebytes = new byte[nbytes];
System.Runtime.InteropServices.Marshal.Copy(pointer, imagebytes, 0, nbytes);
double red;
double green;
double blue;
double gray;
var _grayscale_array = new Double[bitmap_data.Height, bitmap_data.Width];
if (pixelsize >= 3 ) {
for (int I = 0; I < bitmap_data.Height; I++) {
for (int J = 0; J < bitmap_data.Width; J++ ) {
int position = (I * bitmap_data.Stride) + (J * pixelsize);
blue = imagebytes[position];
green = imagebytes[position + 1];
red = imagebytes[position + 2];
gray = 0.299 * red + 0.587 * green + 0.114 * blue;
_grayscale_array[I,J] = gray;
}
}
}
_foto_franjas_original.UnlockBits(bitmap_data);
return _grayscale_array;
}

Use Webcam as ambient light sensor in PC

Is it possible to make the webcam of a pc acting as an ambient light sensor?
I am using .Net 4.5 framework in Windows 8 pro.
Philippe's answer to this question: How to calculate the average rgb color values of a bitmap has code that will calculate the average RGB values of a bitmat image (bm in his code):
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = dstData.Scan0;
long[] totals = new long[] {0,0,0};
int width = bm.Width;
int height = bm.Height;
unsafe
{
byte* p = (byte*) (void*) Scan0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
for (int color = 0; color < 3; color++)
{
int idx = (y*stride) + x*4 + color;
totals[color] += p[idx];
}
}
}
}
int avgR = totals[0] / (width*height);
int avgG = totals[1] / (width*height);
int avgB = totals[2] / (width*height);
Once you have the average RGB values you can use Color.GetBrightness to determine how light or dark it is. GetBrightness will return a number between 0 and 1, 0 is black, 1 is white. You can use something like this:
Color imageColor = Color.FromARGB(avgR, avgG, avgB);
double brightness = imageColor.GetBrightness();
You could also convert the RGB values to HSL and look at the "L", that may be more accurate, I don't know.

Multiply two images in C# as multiply two layers in Photoshop

I have two images and I want to multiply these two images together in C# as we multiply two layers in Photoshop.
I have found the method by which the layers are multiplied in photoshop or any other application.
Following is the formula that I have found on GIMP documentation. It says that
E=(M*I)/255
where M and I are the color component(R,G,B) values of the two layers. We have to apply this to every color component. E will be the resultant value for that color component.
If the color component values are >255 then it should be set to white i.e. 255 and if it is <0 then it should be set as Black i.e. 0
Here I have a suggestion - I didn't test it, so sorry for any errors - I'm also assuming that both images have the same size and are greylevel.
Basically I'm multiplying the image A for the relative pixel percentage of image B.
You can try different formulas like:
int result = ptrB[0] * ( (ptrA[0] / 255) + 1);
or
int result = (ptrB[0] * ptrA[0]) / 255;
Never forget to check for overflow (above 255)
public void Multiply(Bitmap srcA, Bitmap srcB, Rectangle roi)
{
BitmapData dataA = SetImageToProcess(srcA, roi);
BitmapData dataB = SetImageToProcess(srcB, roi);
int width = dataA.Width;
int height = dataA.Height;
int offset = dataA.Stride - width;
unsafe
{
byte* ptrA = (byte*)dataA.Scan0;
byte* ptrB = (byte*)dataB.Scan0;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x, ++ptrA, ++ptrB)
{
int result = ptrA[0] * ( (ptrB[0] / 255) + 1);
ptrA[0] = result > 255 ? 255 : (byte)result;
}
ptrA += offset;
ptrB += offset;
}
}
srcA.UnlockBits(dataA);
srcB.UnlockBits(dataB);
}
static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
{
if (image != null)
return image.LockBits(
roi,
ImageLockMode.ReadWrite,
image.PixelFormat);
return null;
}

Categories