I'm writing a program that removes an overlay from a png image using the mask (the overlay image)
having image 1 and 2 I want to achive image 3.
I have tried using lockbits and tried many things but I can't do the math right I think
rgbValues is the byte array of overlay and rgbValues2 is the byte array of given image.
for (int counter = 0; counter < rgbValues.Length; counter ++)
{
int x = (counter / 4) * 4;
if (rgbValues[x + 3] != 0)
{
if (rgbValues[x + 3] == rgbValues2[x + 3])
{
rgbValues2[counter] = 0;
}
else
{
float a1 = (float)rgbValues[counter];
float a2 = (float)rgbValues2[counter] ;
float b1 = (float)rgbValues[x + 3];
float b2 = (float)rgbValues2[x + 3];
rgbValues2[counter] = (byte)(2 * a2- a1);
}
}
}
I've tried this with your sample Images, although they are composed in the same big image and looks like it works. The following code doesn't use LockBits for simplicity, it just gives you the idea, that is how to calculate the base color (in the third image) from the blend color (in the second image) and the result color (in the first image):
public Image ExtractBaseImage(Bitmap resultImage, Bitmap blendImage) {
Bitmap bm = new Bitmap(resultImage.Width, resultImage.Height);
for (int i = 0; i < resultImage.Width; i++) {
for (int j = 0; j < resultImage.Height; j++) {
Color resultColor = resultImage.GetPixel(i, j);
Color blendColor = blendImage.GetPixel(i, j);
if (blendColor.A == 0) bm.SetPixel(i, j, resultColor);
else if(blendColor != resultColor){
float opacity = blendColor.A / 255f;
int r = Math.Max(0,Math.Min(255,(int) ((resultColor.R - (opacity) * blendColor.R) / (1-opacity))));
int g = Math.Max(0,Math.Min(255,(int)((resultColor.G - (opacity) * blendColor.G) / (1-opacity))));
int b = Math.Max(0,Math.Min(255,(int)((resultColor.B - (opacity) * blendColor.B) / (1-opacity))));
bm.SetPixel(i,j,Color.FromArgb(r,g,b));
}
}
}
return bm;
}
Usage: Suppose the images are numbered as you did to the images posted in your question, we have image1, image2, image3 variables:
image3 = ExtractBaseImage((Bitmap)image1, (Bitmap)image2);
Related
I'd like to figure out how I can add a 1 pixel border in between each frame that my spritesheet creator generates, can anyone help?
The function below takes an array of images and turns them into a table filled with bitmaps.
The program is supposed to take a gif and turn it into multiple spritesheets, so that it can be displayed in a game engine. That part is working fine, but I'd like to add a 1 pixel border in between each frame.
public Bitmap[] combineFrames(Image[] images, int columns)
{
int rows = columns;
int imagesPerSheet = rows * columns;
int sheets = (int)Math.Ceiling((double)images.Length / imagesPerSheet);
Bitmap[] mapTable = new Bitmap[55]; //55 being the max #sheets
for (int x = 0; x < sheets; x++)
{
Bitmap bitmap = new Bitmap(images[0].Width * columns, images[0].Height * rows);
Graphics graphics = Graphics.FromImage(bitmap);
int L = 0;
int remainingNFrames = (images.Length - (imagesPerSheet * x));
if (remainingNFrames < imagesPerSheet)
{
L = remainingNFrames;
}
else
{
L = imagesPerSheet;
}
for (int i = 0; i <= L; i++)
{
int formulizedProduct = imagesPerSheet * (x) + (i);
if (formulizedProduct == images.Length) { break; } //should always be length - 1
Image image = images[formulizedProduct]; //16*x+i
int X = (image.Width * ((i - 1) % columns));
int Y = (image.Height * (int)((double)(i - 1) / columns));
graphics.DrawImage(
image,
X, //I feel like I'd have to adjust this part and the part below, but I'm not really sure.
Y
);
}
mapTable[x] = bitmap;
}
return mapTable;
}
Thanks.
Converting a bitmap to grayscale is pretty easy with AForge:
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
// first convert to a grey scale image
var filterGreyScale = new Grayscale(0.2125, 0.7154, 0.0721);
me = filterGreyScale.Apply(me);
return me;
}
But I need something more tricky:
Imagine you want to convert everything to grayscale except for a circle in the middle of the bitmap. In other words: a circle in the middle of the given bitmap should keep its original colours.
Let's assume the radius of the circle is 20px, how should I approach this?
This can be accomplished using MaskedFilter with a mask that defines the circled area you describe. As the documentation states
Mask can be specified as .NET's managed Bitmap, as UnmanagedImage or
as byte array. In the case if mask is specified as image, it must be 8
bpp grayscale image. In all case mask size must be the same as size of
the image to process.
So the mask image has to be generated based on the source image's width and height.
I haven't compiled the following code but it should get you on your way. If the circle is always in the same spot, you could generate the image mask outside the method so that it doesn't have to be regenerated each time you apply the filter. Actually you could have the whole MaskedFilter generated outside the method that applies it if nothing changes but the source image.
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
var radius = 20, x = me.Width / 2, y = me.Height / 2;
using (Bitmap maskImage = new Bitmap(me.Width, me.Height, PixelFormat.Format8bppIndexed))
{
using (Graphics g = Graphics.FromImage(maskImage))
using (Brush b = new SolidBrush(ColorTranslator.FromHtml("#00000000")))
g.FillEllipse(b, x, y, radius, radius);
var maskedFilter = new MaskedFilter(new Grayscale(0.2125, 0.7154, 0.0721), maskImage);
return maskedFilter.Apply(me);
}
}
EDIT
The solution for this turned out to be a lot more trickier than I expected. The main problem was that the MaskedFilter doesn't allow the usage of filters that change the images format, which the Grayscale filter does (it changes the source to an 8bpp or 16 bpp image).
The following is the resulting code, which I have tested, with comments added to each part of the ConvertToGrayScale method explaining the logic behind it. The gray-scaled portion of the image has to be converted back to RGB since the Merge filter doesn't support merging two images with different formats.
static class MaskedImage
{
public static void DrawCircle(byte[,] img, int x, int y, int radius, byte val)
{
int west = Math.Max(0, x - radius),
east = Math.Min(x + radius, img.GetLength(1)),
north = Math.Max(0, y - radius),
south = Math.Min(y + radius, img.GetLength(0));
for (int i = north; i < south; i++)
for (int j = west; j < east; j++)
{
int dx = i - y;
int dy = j - x;
if (Math.Sqrt(dx * dx + dy * dy) < radius)
img[i, j] = val;
}
}
public static void Initialize(byte[,] arr, byte val)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = val;
}
public static void Invert(byte[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = (byte)~arr[i, j];
}
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
int radius = 20, x = me.Width / 2, y = me.Height / 2;
// Generate a two-dimensional `byte` array that has the same size as the source image, which will be used as the mask.
byte[,] mask = new byte[me.Height, me.Width];
// Initialize all its elements to the value 0xFF (255 in decimal).
Initialize(mask, 0xFF);
// "Draw" a circle in the `byte` array setting the positions inside the circle with the value 0.
DrawCircle(mask, x, y, radius, 0);
var grayFilter = new Grayscale(0.2125, 0.7154, 0.0721);
var rgbFilter = new GrayscaleToRGB();
var maskFilter = new ApplyMask(mask);
// Apply the `Grayscale` filter to everything outside the circle, convert the resulting image back to RGB
Bitmap img = rgbFilter.Apply(grayFilter.Apply(maskFilter.Apply(me)));
// Invert the mask
Invert(mask);
// Get only the cirle in color from the original image
Bitmap circleImg = new ApplyMask(mask).Apply(me);
// Merge both the grayscaled part of the image and the circle in color in a single one.
return new Merge(img).Apply(circleImg);
}
}
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");
The idea is building a windows form application in Visual Studio 2010 using C#.
The program will run a series of operation when the user hit a button.
Is it possible to use a image to show the progress instead of using a progress bar?
So the idea is that the image will start of being invisible, and as the program progress, the image become more and more visible.
0% - invisible
50% - half transparent
100% - visible
I know you can toggle the PictureBox to be visible or not (PictureBox.Visible = true or false;), but is there a way to make it in between?
Any idea is appreciate.
Manipulating images in winforms is slow, so do this as little as possible:
public Bitmap ImageFade( Bitmap sourceBitmap, byte Transparency)
{
BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
byte blue = 0;
byte green = 0;
byte red = 0;
byte a = 0;
int byteOffset = 0;
for (int offsetY = 0; offsetY <
sourceBitmap.Height; offsetY++)
{
for (int offsetX = 0; offsetX <
sourceBitmap.Width; offsetX++)
{
blue = 0;
green = 0;
red = 0;
a = 0;
byteOffset = offsetY *
sourceData.Stride +
offsetX * 4;
blue += pixelBuffer[byteOffset];
green += pixelBuffer[byteOffset + 1];
red += pixelBuffer[byteOffset + 2];
a += Transparency;//pixelBuffer[byteOffset + 3];
resultBuffer[byteOffset] = blue;
resultBuffer[byteOffset + 1] = green;
resultBuffer[byteOffset + 2] = red;
resultBuffer[byteOffset + 3] = a;
}
}
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0,
resultBitmap.Width, resultBitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap;
}
I note another answer uses SetPixel. Avoid using that function if possible. It's much faster to edit the underlying bytestream, which is still slow since it's not hardware accelerated, but it's the best of several not-great options
This function can potentially be further optimized, but I leave that as an exercise for the reader
You could always adjust the alpha component of your image:
void SetImageProgress(float percent, Bitmap img)
{
int alpha = (int)(percent / 100.0f * 255.0f);
alpha &= 0xff;
for(int x = 0; x < img.Width; x++)
{
for(int y = 0; y < img.Height; y++)
{
Color c = img.GetPixel(x, y);
c = Color.FromArgb(alpha, c.R, c.G, c.B);
img.SetPixel(x, y, c);
}
}
}
I have a .png with transparency that I need to desaturate. I read I need to take the average R,G and B value of the bitmap then use:
G*.59
R*.3
B*.11
I calculate the average color in this way:
private Color Average_Color(Bitmap bitmap) {
Color c = new Color();
int pixel_number = 0;
int r = 0;
int g = 0;
int b = 0;
for (int i = 0; i < bitmap.Width; i++) {
for (int j = 0; j < bitmap.Height; j++) {
c = bitmap.GetPixel(i, j);
r += c.R;
g += c.G;
b += c.B;
pixel_number++;
}
}
c = Color.FromArgb(1, r / pixel_number, g / pixel_number, b / pixel_number);
return c;
}
Then, first to paint my texture, I set the Color in this way:
rgb = Average(bitmap);
GL.Color3(rgb.R * 0.59, rgb.G * 0.3, rgb.B * 0.11);
//here I draw my texture
I don't know why but it doesn't work (I get the texture with his original colors). I guess it's something wrong in Average_Color. Maybe because it's not a total opaque bitmap?
OpenGL expects its colors to be floats normalized from 0 to 1, but I suspect that your bitmap is reporting colors from 0-255.
Therefore I assume you're passing RGB values much greater than 1 to glColor, which get clamped to 1, so your texture looks the same.
Try
GL.Color3(rgb.R*0.59/255.f, rgb.G*0.3/255.f, rgb.B*0.11/255.f);
Try casting your variables to floats before the divide