Using C# I was trying to devide a photo in more than Photo. in Each Photo i have a piece colour.
The way I am doing it have a problem and need your kind advice.
the Problem is my Photo name will have an 8 ARGB Values! but i want just 6 values! i want the filename to take into account the color RGB value without the alpha.
public class PNG2PNGS
{
public static void Convert(string sourcePath)
{
foreach (var fileName in Directory.GetFiles(sourcePath, "*.png"))
{
Bitmap bitmap = new Bitmap(fileName);
List<Color> colors = new List<Color>();
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
Color color = bitmap.GetPixel(i, j);
if (!colors.Contains(color))
{
colors.Add(color);
}
}
}
foreach (Color color in colors)
{
Bitmap bitmap2 = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat);
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
if (bitmap.GetPixel(i, j) == color)
{
bitmap2.SetPixel(i, j, color);
}
else
{
bitmap2.SetPixel(i, j, Color.Transparent);
}
}
}
bitmap2.Save(Path.Combine(Path.GetDirectoryName(fileName), $"{Path.GetFileNameWithoutExtension(fileName)}_{color.ToArgb().ToString("X6")}.png"), ImageFormat.Png);
}
bitmap.GetPixel(0,0);
}
}
}
If I understood correctly, you want the filename to take into account the color RGB value without the alpha.
You can use a temporary that will be exactly that - the color with 0 for the alpha channel.
Then use your print format specifier as you already did:
Color color = Color.FromArgb(0x0A, 0x0B, 0x0C, 0x0D);
//...
Color colorNoAlpha = Color.FromArgb(0, color.R, color.G, color.B);
Console.WriteLine(colorNoAlpha.ToArgb().ToString("X6"));
Output:
0B0C0D
(In your code, add the line initializing colorNoAlpha before saving the file, and in the file save statement replace color with colorNoAlpha).
Related
I wrote some code to show an array of bytes as an image. There is an array of bytes in which every element represents a value of 8-bit gray scale image. Zero equals the most black and 255 does the most white pixel. My goal is to convert this w*w-pixel gray-scale image to some thing accepted by pictureBox1.Image.
This is my code:
namespace ShowRawImage
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int i = 0, j = 0, w = 256;
byte[] rawIm = new byte[256 * 256];
for(i = 0; i < w; ++i)
{
for (j = 0; j < w; ++j)
{
rawIm[i * w + j] = (byte)j; // BitConverter.GetBytes(j);
}
}
MemoryStream mStream = new MemoryStream();
mStream.Write(rawIm, 0, Convert.ToInt32(rawIm.Length));
Bitmap bm = new Bitmap(mStream, false);// the error occurs here
mStream.Dispose();
pictureBox1.Image = bm;
}
}
}
However I get this error:
Parameter is not valid.
The error snapshot
where is my mistake?
EDIT:
In next step I am going to display 16-bit grayscale images.
The Bitmap(Stream, bool) constructor expects a stream with an actual image format (eg. PNG, GIF, etc.) along with header, palette, and possibly compressed image data.
To create a Bitmap from raw data, you need to use the Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) constructor, but that is also quite inconvenient because you need a pinned raw data that you can pass as scan0.
The best if you just create an 8bpp bitmap with grayscale palette and set the pixels manually:
var bmp = new Bitmap(256, 256, PixelFormat.Format8bppIndexed);
// making it grayscale
var palette = bmp.Palette;
for (int i = 0; i < 255; i++)
palette.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = palette;
Now you can access its raw content as bytes where 0 is black and 255 is white:
var bitmapData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int y = 0; y < bitmapData.Height; y++)
{
for (int x = 0; x < bitmapData.Width; x++)
{
unsafe
{
((byte*) bitmapData.Scan0)[y * bitmapData.Stride + x] = (byte)x;
}
}
}
bmp.UnlockBits(bitmapData);
The result image:
But if you don't want to use unsafe code, or you want to set pixels by colors, you can use this library (disclaimer: written by me) that supports efficient manipulation regardless of the actual PixelFormat. Using that library the last block can be rewritten like this:
using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
IWritableBitmapDataRow row = bitmapData.FirstRow;
do
{
for (int x = 0; x < bitmapData.Width; x++)
row[x] = Color32.FromGray((byte)x); // this works for any pixel format
// row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
} while (row.MoveNextRow());
}
Or like this, using Parallel.For (this works only because in your example all rows are the same so the image is a horizontal gradient):
using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
Parallel.For(0, bitmapData.Height, y =>
{
var row = bitmapData[y];
for (int x = 0; x < bitmapData.Width; x++)
row[x] = Color32.FromGray((byte)x); // this works for any pixel format
// row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
});
}
As said in the comments - bitmap is not just an array. So to reach your goal you can create bitmap of needed size and set pixels with Bitmap.SetPixel:
Bitmap bm = new Bitmap(w, w);
for(var i = 0; i < w; ++i)
{
for (var j = 0; j < w; ++j)
{
bm.SetPixel(i,j, Color.FromArgb(j, j, j));
}
}
I am trying to get color from specific area in an Image.
Assume that , this is image , and I want to get color inside image.(the result should be red of the above image) This color may be different position in image. Because I don't know exact position of color where it starting, so I can't get exact result.
Until now, I cropped image giving manually position of x and y, and then cropped image and I got average color of cropped image. But I know , this is not exact color.
What I tried :
private RgbDto GetRGBvalueCroppedImage(Image croppedImage)
{
var avgRgb = new RgbDto();
var bm = new Bitmap(croppedImage);
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.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];
}
}
}
}
avgRgb.avgB = (int)totals[0] / (width * height);
avgRgb.avgG = (int)totals[1] / (width * height);
avgRgb.avgR = (int)totals[2] / (width * height);
return avgRgb;
}
How can I get exact position to crop? May be I can convert image to byte array, then I can find different color and take position of it and then crop. But I have no clue how do this.
You can use something this extension method to get dominant color in a region of an image in case they are not all the same
public static Color GetDominantColor(this Bitmap bitmap, int startX, int startY, int width, int height) {
var maxWidth = bitmap.Width;
var maxHeight = bitmap.Height;
//TODO: validate the region being requested
//Used for tally
int r = 0;
int g = 0;
int b = 0;
int totalPixels = 0;
for (int x = startX; x < (startX + width); x++) {
for (int y = startY; y < (startY + height); y++) {
Color c = bitmap.GetPixel(x, y);
r += Convert.ToInt32(c.R);
g += Convert.ToInt32(c.G);
b += Convert.ToInt32(c.B);
totalPixels++;
}
}
r /= totalPixels;
g /= totalPixels;
b /= totalPixels;
Color color = Color.FromArgb(255, (byte)r, (byte)g, (byte)b);
return color;
}
You can then use it like
Color pixelColor = myBitmap.GetDominantColor(xPixel, yPixel, 5, 5);
there is room for improvement, like using a Point and Size, or even a Rectangle
public static Color GetDominantColor(this Bitmap bitmap, Rectangle area) {
return bitmap.GetDominantColor(area.X, area.Y, area.Width, area.Height);
}
and following this link:
https://www.c-sharpcorner.com/UploadFile/0f68f2/color-detecting-in-an-image-in-C-Sharp/
If you want to get the image colors, you don't need to do any cropping at all. Just loop on image pixels and find the two different colors. (Assuming that you already know the image will have exactly 2 colors, as you said in comments). I've written a small function that will do that. However, I didn't test it in an IDE, so expect some small mistakes:
private static Color[] GetColors(Image image)
{
var bmp = new Bitmap(image);
var colors = new Color[2];
colors[0] = bmp.GetPixel(0, 0);
for (int i = 0; i < bmp.Width; i++)
{
for (int j = 0; j < bmp.Height; j++)
{
Color c = bmp.GetPixel(i, j);
if (c == colors[0]) continue;
colors[1] = c;
return colors;
}
}
return colors;
}
I have some scanned image. I want to drop all color except black.
Problem is black color is not always rgb(0,0,0). So how can I do that?
Thanks.
You can use this code:
Bitmap myBitmap = new Bitmap(#"YourPath");
const float limit = 0.3f;
for (int i = 0; i < myBitmap.Width; i++)
{
for (int j = 0; j < myBitmap.Height; j++)
{
Color c = myBitmap.GetPixel(i, j);
if (c.GetBrightness() > limit)
{
myBitmap.SetPixel(i, j, Color.White);
}
}
}
myBitmap.Save(#"YourNewPath");
And play with limit to get what you want.
I am trying to make an image black and white in c#. Here is my code:
public static void SetGrayscale(Bitmap b)
{
Bitmap temp = (Bitmap) b;
Bitmap bmap = (Bitmap)temp.Clone();
Color c;
for (int i = 0; i < bmap.Width; i++)
{
for (int j = 0; j < bmap.Height; j++)
{
c = bmap.GetPixel(i, j);
byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
}
}
b = (Bitmap)bmap.Clone();
}
static void Main(string[] args)
{
Bitmap bm = new Bitmap(Image.FromFile("D:\\users\\visual studio 2010\\Projects\\aaa\\20130924_144411.tif"));
byte[] pixels = ImageToByte(bm);
SetGrayscale(bm);
}
The problem is, it does not turn into black and white, it is still the same. Am i not saving the changed image? What can be the problem here?
Thanks
Are you refering to that the file on disk doesn't change? You would have to save the grayscaled bitmap: bm.Save("D:\\bitmap.tif");
i have a black/white line-sensor wich gives me the grayscale values (0-255) of scanned data. i want to save this values as Bitmap (.bmp) and also show them in a picturebox. my current code looks like this:
PixelFormat px = PixelFormat.Canonical;
Bitmap bitmap = new Bitmap(width, height, px);
int[,] GreyScaleArray2D = new int[width,height];
for (int yy = 0; yy < (lb_SpReceivedData.Count-width); yy += width)
{
for (int xx = 0; xx < width; xx++)
{
GreyScaleArray2D[xx,tmp] = lb_SpReceivedData[(yy+xx)];
}
tmp++;
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int red = GreyScaleArray2D[x,y]; // read from array
int green = GreyScaleArray2D[x,y]; // read from array
int blue = GreyScaleArray2D[x,y]; // read from array
bitmap.SetPixel(x, y, Color.FromArgb(0, red, green, blue));
}
}
pictureBox1.Image = bitmap;
Question) i get an error of invalid parameter if i pass
PixelFormat.Canonical
to the bitmap constructor.
Question) even though i pass the bitmap to my picturebox there is nothing to see in the picturebox! what am i doing wrong?
thanks