Lighten/Darken/Transparent part of image - c#

I'm loading up an image into a BitmapImage and convert this into an array of PixelColor's. What I would like is to be able to manipulate some of these pixels to make them Lighter/Darker/Transparent but I can't seem to get it to work. It does work to set it to a certain color but the alpha channel is ignored.
It's a WPF app using C#4
Thanks!
And the code...
namespace BitmapTest {
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Image = System.Windows.Controls.Image;
public partial class MainWindow {
public MainWindow() {
InitializeComponent();
LoadImage();
}
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
public static BitmapSource LoadBitmap(Bitmap source) {
var ip = source.GetHbitmap();
try {
return Imaging.CreateBitmapSourceFromHBitmap(ip,
IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
} finally {
DeleteObject(ip);
}
}
private void LoadImage() {
var i = new Image();
var src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri("road.jpg", UriKind.Relative);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
var pixels = GetPixels(src);
for (var x = 0; x < Math.Min(50, pixels.GetLength(0)); x++) {
for (var y = 0; y < Math.Min(50, pixels.GetLength(1)); y++) {
pixels[x, y] = new PixelColor {Alpha = 100, Red = pixels[x, y].Red, Green = pixels[x, y].Green, Blue = pixels[x, y].Blue};
}
}
var bitmap = new WriteableBitmap(src.PixelWidth, src.PixelHeight, src.DpiX, src.DpiY, src.Format, src.Palette);
PutPixels(bitmap, pixels, 0, 0);
i.Source = bitmap;
i.Stretch = Stretch.Fill;
RootGrid.Children.Add(i);
}
public PixelColor[,] GetPixels(BitmapSource source) {
if (source.Format != PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
var pixels = source.CopyPixels();
return pixels;
}
public void PutPixels(WriteableBitmap bitmap, PixelColor[,] pixels, int x, int y) {
var width = pixels.GetLength(0);
var height = pixels.GetLength(1);
var sourceRect = new Int32Rect(0, 0, width, height);
bitmap.WritePixels(sourceRect, pixels, width*4, x, y);
}
[StructLayout(LayoutKind.Sequential)]
public struct PixelColor {
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
}
public static class BitmapSourceHelper {
public static MainWindow.PixelColor[,] CopyPixels(this BitmapSource source) {
if (source.Format != PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
var pixels = new MainWindow.PixelColor[source.PixelWidth, source.PixelHeight];
var stride = source.PixelWidth * ((source.Format.BitsPerPixel + 7) / 8);
var pinnedPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
pinnedPixels.AddrOfPinnedObject(),
pixels.GetLength(0) * pixels.GetLength(1) * 4,
stride);
pinnedPixels.Free();
return pixels;
}
}
}

Here is an example of lightening and darkening an image in a windows forms app. The code here is based the Brightness filter section of an article on The Code Project site: Image Processing for Dummies with C# and GDI+ Part 1.
public Bitmap Lighten(Bitmap bitmap, int amount)
{
if (amount < -255 || amount > 255)
return bitmap;
// GDI+ still lies to us - the return format is BGR, NOT RGB.
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
int nVal = 0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - bitmap.Width * 3;
int nWidth = bitmap.Width * 3;
for (int y = 0; y < bitmap.Height; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
nVal = (int)(p[0] + amount);
if (nVal < 0) nVal = 0;
if (nVal > 255) nVal = 255;
p[0] = (byte)nVal;
++p;
}
p += nOffset;
}
}
bitmap.UnlockBits(bmData);
return bitmap;
}
private void btnLighten_Click(object sender, EventArgs e)
{
Bitmap image = pictureBox1.Image as Bitmap;
pictureBox1.Image = Lighten(image, 10);
}
private void btnDarken_Click(object sender, EventArgs e)
{
Bitmap image = pictureBox1.Image as Bitmap;
pictureBox1.Image = Lighten(image, -10);
}

This code would adjusts a given rectangle in the image, according to brightness, contrast, alpha, gamma:
Bitmap clonedImage = originalImage.Clone(rect, originalImage.PixelFormat);
float gamma = 1.0f; // no change in gamma
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},
new float[] {adjustedBrightness, adjustedBrightness, adjustedBrightness, 0, 1}};
var imageAttributes = new ImageAttributes();
imageAttributes.ClearColorMatrix();
imageAttributes.SetColorMatrix(new ColorMatrix(ptsArray), ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
imageAttributes.SetGamma(gamma, ColorAdjustType.Bitmap);
// Copy back to the original image from the cloned image
Graphics g = Graphics.FromImage(originalImage);
g.DrawImage(clonedImage, new Rectangle(0, 0, clonedImage.Width, clonedImage.Height)
, rect.left, rect.top, rect.Width, rect.Height,
GraphicsUnit.Pixel, imageAttributes);
g.Flush();

Related

How to draw the diffrenet pixels in yellow only when the pie is rotating above this pixels?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Doppler_Radar
{
public partial class Form1 : Form
{
int myPiePercent = 15;
float distanceFromCenterPixels;
float distanceFromCenterKm = 200F;
public Form1()
{
InitializeComponent();
pictureBox1.Image = Image.FromFile(#"D:\New folder (4)\Weather Radar\WithClouds.bmp");
timer1.Enabled = true;
distanceFromCenterPixels = (float)(183d * (double)distanceFromCenterKm / 200d);
pictureBox1.Image = CalcDifference(new Bitmap(pictureBox1.Image),
new Bitmap(#"D:\New folder (4)\Weather Radar\WithoutClouds.bmp"));
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
DrawPieOnPicturebox(e.Graphics);
}
public void DrawPieOnPicturebox(Graphics myPieGraphic)
{
Color myPieColors = Color.FromArgb(150, Color.LightGreen);
Size myPieSize = new Size((int)distanceFromCenterPixels, (int)distanceFromCenterPixels);
Point myPieLocation = new Point((pictureBox1.Width - myPieSize.Width) / 2, (pictureBox1.Height - myPieSize.Height) / 2);
DrawMyPie(myPiePercent, myPieColors, myPieGraphic, myPieLocation, myPieSize);
}
public void DrawMyPie(int myPiePerecent, Color myPieColor, Graphics myPieGraphic, Point
myPieLocation, Size myPieSize)
{
using (SolidBrush brush = new SolidBrush(myPieColor))
{
myPieGraphic.FillPie(brush, new Rectangle(myPieLocation, myPieSize), Convert.ToSingle(myPiePerecent * 360 / 100), Convert.ToSingle(15 * 360 / 100));
}
}
private void timer1_Tick(object sender, EventArgs e)
{
myPiePercent++;
pictureBox1.Invalidate();
}
public Bitmap CalcDifference(Bitmap bmp1, Bitmap bmp2)
{
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bitmapdata = bmp1.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
IntPtr source = bitmapdata.Scan0;
BitmapData data2 = bmp2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
IntPtr ptr2 = data2.Scan0;
int length = (bmp1.Width * bmp1.Height) * 4;
byte[] destination = new byte[length];
byte[] buffer2 = new byte[length];
Marshal.Copy(source, destination, 0, length);
Marshal.Copy(ptr2, buffer2, 0, length);
for (int i = 0; i < length; i += 4)
{
if (((destination[i] == buffer2[i]) && (destination[i + 1] == buffer2[i + 1])) && (destination[i + 2] == buffer2[i + 2]))
{
destination[i] = 0xff;
destination[i + 1] = 0xff;
destination[i + 2] = 0xff;
destination[i + 3] = 0;
}
else
{
destination[i] = 0;
destination[i + 1] = 255;
destination[i + 2] = 255;
}
}
Marshal.Copy(destination, 0, source, length);
bmp1.UnlockBits(bitmapdata);
bmp2.UnlockBits(data2);
return bmp1;
}
}
}
this color in the yellow the diffrenet pixels all the time because i'm calling the method CalcDifference in the constructor. but what i want is that only when the rotating pie is over/above this diffrenet pixels then color this diffrenet pixels that are under the pie and not all the diffrenet pixels.
on the left side is when coloring the diffrenet pixels with the method CalcDifference.
on the right side the image at it's original.
the goal is to make a doppler radar effect that detect the clouds when the pie is rotating abvoe them.
Yes, you can use a little trick to change the colors of the clouds under the pie and skip the others.
To optimize your CalcDifference method and speed things up, get from each image only the pie region to process. The rest of the image does not change and you don't need to traverse the data of that region.
Pass the clipped images to the CalcDifference method and change the colors of the clouds in that region. All of them. Note, I have changed the method to convert the original images to 32bppArgb images if needed so you can pass images of different formats that the LockBits supports to process.
Assign the full-size image with clouds to the .Image property of the PictureBox.
Here's the trick. Handle the PictureBox.Paint event and create a GraphicsPath object, add the pie and pass it to Graphics.SetClip method. Draw the yellow clouds image then reset the clip (Graphics.ResetClip) to draw the pie.
public partial class SomeForm : Form
{
private float startAngle;
private readonly SolidBrush brPie;
private Rectangle pieRect;
private Bitmap imgWithYellowClouds;
public SomeForm()
{
InitializeComponent();
brPie = new SolidBrush(Color.FromArgb(150, Color.LightGreen));
picWithoutClouds.Image = Image.FromFile(#"...");
picWithClouds.Image = picCanvas.Image = Image.FromFile(#"...");
CreateYellowCloudsImage();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
brPie.Dispose();
imgWithYellowClouds?.Dispose();
}
private void picCanvas_Resize(object sender, EventArgs e) =>
CreateYellowCloudsImage();
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
if (pieRect.Width < 1 || pieRect.Height < 1 ||
imgWithYellowClouds == null) return;
var g = e.Graphics;
using (var gp = new GraphicsPath())
{
gp.AddPie(pieRect, startAngle, 360f / 8);
g.SetClip(gp);
g.DrawImage(imgWithYellowClouds, pieRect);
g.ResetClip();
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(brPie, gp);
}
}
private void SomeButton(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
private void timer1_Tick(object sender, EventArgs e)
{
// Change as needed...
startAngle = (startAngle + 10) % 360;
picCanvas.Invalidate();
}
public Bitmap CalcDifference(Bitmap bmp1, Bitmap bmp2)
{
var disImg1 = !Is32bppArgbFormat(bmp1);
var disImg2 = !Is32bppArgbFormat(bmp2);
var img1 = disImg1 ? To32bppArgbFormat(bmp1) : new Bitmap(bmp1);
var img2 = disImg2 ? To32bppArgbFormat(bmp2) : bmp2;
var img1Data = img1.LockBits(
new Rectangle(Point.Empty, img1.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
var img2Data = img2.LockBits(
new Rectangle(Point.Empty, img2.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
var img1Buffer = new byte[img1Data.Stride * img1Data.Height];
var img2Buffer = new byte[img2Data.Stride * img2Data.Height];
Marshal.Copy(img1Data.Scan0, img1Buffer, 0, img1Buffer.Length);
Marshal.Copy(img2Data.Scan0, img2Buffer, 0, img2Buffer.Length);
img2.UnlockBits(img2Data);
for (int i = 0; i + 4 < img1Buffer.Length; i += 4)
{
if (img1Buffer[i] == img2Buffer[i] &&
img1Buffer[i + 1] == img2Buffer[i + 1] &&
img1Buffer[i + 2] == img2Buffer[i + 2])
{
img1Buffer[i] = 0;
img1Buffer[i + 1] = 0;
img1Buffer[i + 2] = 0;
img1Buffer[i + 3] = 0;
}
else
{
img1Buffer[i] = 0;
img1Buffer[i + 1] = 255;
img1Buffer[i + 2] = 255;
}
}
Marshal.Copy(img1Buffer, 0, img1Data.Scan0, img1Buffer.Length);
img1.UnlockBits(img1Data);
if (disImg2) img2.Dispose();
return img1;
}
private bool Is32bppArgbFormat(Bitmap bmp) =>
Image.GetPixelFormatSize(bmp.PixelFormat) == 32 &&
bmp.PixelFormat != PixelFormat.Indexed;
private Bitmap To32bppArgbFormat(Bitmap src)
{
var bmp = new Bitmap(src.Width, src.Height, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bmp))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(src, new Rectangle(0, 0, bmp.Width, bmp.Height),
0, 0, src.Width, src.Height, GraphicsUnit.Pixel);
return bmp;
}
}
private void CreateYellowCloudsImage()
{
var cs = picCanvas.ClientSize;
// Change as needed...
var sz = new Size(128, 128);
pieRect = new Rectangle(
(cs.Width - sz.Width) / 2,
(cs.Height - sz.Height) / 2,
sz.Width, sz.Height);
// To get the image rectangle regardless of the SizeMode property
// so we can get the pie's region to process...
var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
BindingFlags.NonPublic | BindingFlags.Instance);
var imageRect = (Rectangle)method.Invoke(picCanvas,
new object[] { picCanvas.SizeMode });
var cx = picCanvas.Image.Width / (float)imageRect.Width;
var cy = picCanvas.Image.Height / (float)imageRect.Height;
var r2 = RectangleF.Intersect(imageRect, pieRect);
r2.Offset(-imageRect.X, -imageRect.Y);
var cloneRect = new RectangleF(
r2.X * cx,
r2.Y * cy,
r2.Width * cx,
r2.Height * cy);
imgWithYellowClouds?.Dispose();
imgWithYellowClouds = null;
using (var cloulds = (picWithClouds.Image as Bitmap)
.Clone(cloneRect, picWithClouds.Image.PixelFormat))
using (var noClouds = (picWithoutClouds.Image as Bitmap)
.Clone(cloneRect, picWithoutClouds.Image.PixelFormat))
{
// The yellow clouds image...
imgWithYellowClouds = CalcDifference(cloulds, noClouds);
}
}
}
Taken from your previous questions for this demo.

C# Image loses color when saved

I'm working in a little application that will send images to a card printer (Datacard brand), this program should convert the image to only two colors (just like black and white) RGB(217,217,217) and pure white. The problem is that even if in my screen the pictures looks converted, when I save/print the image, it looks black and white.
This method converts the image to black and white so I can later replace only the black color with the RGB 217,217,217
public static Bitmap BitmapTo1Bpp(Bitmap img)
{
int w = img.Width;
int h = img.Height;
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h),ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
byte[] scan = new byte[(w + 7) / 8];
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (x % 8 == 0) scan[x / 8] = 0;
Color c = img.GetPixel(x, y);
if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));
}
Marshal.Copy(scan, 0, (IntPtr)((long)data.Scan0 + data.Stride * y), scan.Length);
}
bmp.UnlockBits(data);
return bmp;
}
This method replaces the black color for the new one:
public static Image ImageToRGB217_2(Image image)
{
ColorMatrix colorMatrix = new ColorMatrix(new[]
{
new float[] {0.851f, 0.851f, 0.851f, 1.000f, 0.000f},
new float[] {0.851f, 0.851f, 0.851f, 1.000f, 0.000f},
new float[] {0.851f, 0.851f, 0.851f, 1.000f, 0.000f},
new float[] {0.000f, 0.000f, 0.000f, 0.000f, 0.000f},
new float[] {1.000f, 1.000f, 1.000f, 0.000f, 0.000f}
});
Image img = ApplyColorMatrix(image, colorMatrix);
return img;
}
private static Bitmap ApplyColorMatrix(Image sourceImage, ColorMatrix colorMatrix)
{
Bitmap bmp32BppSource = GetArgbCopy(sourceImage);
Bitmap bmp32BppDest = new Bitmap(bmp32BppSource.Width, bmp32BppSource.Height, PixelFormat.Format32bppArgb);
try
{
using (Graphics graphics = Graphics.FromImage(bmp32BppDest))
{
ImageAttributes bmpAttributes = new ImageAttributes();
bmpAttributes.SetColorMatrix(colorMatrix);
graphics.DrawImage(bmp32BppSource, new Rectangle(0, 0, bmp32BppSource.Width, bmp32BppSource.Height),
0, 0, bmp32BppSource.Width, bmp32BppSource.Height, GraphicsUnit.Pixel, bmpAttributes);
}
bmp32BppSource.Dispose();
}
catch (Exception ex)
{
MessageBox.Show(#"Ocurrió un error al intentar aplicar el filtro UV en una de las imágenes, error:" + ex.Message, "Convertidor", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return bmp32BppDest;
}
private static Bitmap GetArgbCopy(Image sourceImage)
{
Bitmap bmpNew = new Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb);
using (Graphics graphics = Graphics.FromImage(bmpNew))
{
graphics.DrawImage(sourceImage, new Rectangle(0, 0, bmpNew.Width, bmpNew.Height), new Rectangle(0, 0, bmpNew.Width, bmpNew.Height), GraphicsUnit.Pixel);
graphics.Flush();
}
return bmpNew;
}
And here is where I call both:
private void usarFiltroRGB217ToolStripMenuItem_Click(object sender, EventArgs e)
{
pictureBox1.Image = UVImage.ImageToRGB217_2(UVImage.BitmapTo1Bpp((Bitmap)pictureBox1.Image));
pictureBox1.Refresh();
}
This is how the result looks on screen: http://i127.photobucket.com/albums/p128/dawc159951/Preview_zpsxqtzht2a.jpg
But when I save/print, this is what i get:
http://i127.photobucket.com/albums/p128/dawc159951/217_zpsbmuobnrw.png
This is the saving method:
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
string path = saveFileDialog1.FileName;
Bitmap tmp = (Bitmap)pictureBox1.Image;
tmp.Save(path, ImageFormat.Bmp);
if (File.Exists(path))
Process.Start(path);
}
I would appreciate any help to find out why i'm not able to get result i need.
Thanks in advance.

Inverting image returns a black image

I want to invert an Image object. Currently my code looks like this:
private Image Invert(Image img)
{
var bmpPicture = new Bitmap(img.Width, img.Height);
var iaPicture = new ImageAttributes();
var cmPicture = new ColorMatrix { Matrix00 = -1, Matrix11 = -1, Matrix22 = -1 };
iaPicture.SetColorMatrix(cmPicture);
var gfxPicture = Graphics.FromImage(img);
var rctPicture = new Rectangle(0, 0, img.Width, img.Height);
gfxPicture.DrawImage(img, rctPicture, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, iaPicture);
return bmpPicture;
}
However, when I run this, and show it in a PictureBox, the result is a black image. I'm running this in Visual Studio 2012 under Windows 8 Release preview. If there is a better way to do this, please let me know. Thanks.
Try this: http://mariusbancila.ro/blog/2009/11/13/using-colormatrix-for-creating-negative-image/
public Bitmap Transform(Bitmap source)
{
//create a blank bitmap the same size as original
Bitmap newBitmap = new Bitmap(source.Width, source.Height);
//get a graphics object from the new image
Graphics g = Graphics.FromImage(newBitmap);
// create the negative color matrix
ColorMatrix colorMatrix = new ColorMatrix(new float[][]
{
new float[] {-1, 0, 0, 0, 0},
new float[] {0, -1, 0, 0, 0},
new float[] {0, 0, -1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {1, 1, 1, 0, 1}
});
// create some image attributes
ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(colorMatrix);
g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height),
0, 0, source.Width, source.Height, GraphicsUnit.Pixel, attributes);
//dispose the Graphics object
g.Dispose();
return newBitmap;
}
Fast alternative to using a ColorMatrix for pixel manipulation:
public static void BitmapInvertColors(Bitmap bitmapImage)
{
var bitmapRead = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
var bitmapLength = bitmapRead.Stride * bitmapRead.Height;
var bitmapBGRA = new byte[bitmapLength];
Marshal.Copy(bitmapRead.Scan0, bitmapBGRA, 0, bitmapLength);
bitmapImage.UnlockBits(bitmapRead);
for (int i = 0; i < bitmapLength; i += 4)
{
bitmapBGRA[i] = (byte)(255 - bitmapBGRA[i]);
bitmapBGRA[i + 1] = (byte)(255 - bitmapBGRA[i + 1]);
bitmapBGRA[i + 2] = (byte)(255 - bitmapBGRA[i + 2]);
// [i + 3] = ALPHA.
}
var bitmapWrite = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);
Marshal.Copy(bitmapBGRA, 0, bitmapWrite.Scan0, bitmapLength);
bitmapImage.UnlockBits(bitmapWrite);
}
VB version of Dan's Answer. It works like a charm !!
Public Function Transform(source As Bitmap) As Bitmap
'create a blank bitmap the same size as original
Dim newBitmap As New Bitmap(source.Width, source.Height)
'get a graphics object from the new image
Dim g As Graphics = Graphics.FromImage(newBitmap)
' create the negative color matrix
Dim colorMatrix As New ColorMatrix(New Single()() {New Single() {-1, 0, 0, 0, 0}, New Single() {0, -1, 0, 0, 0}, New Single() {0, 0, -1, 0, 0}, New Single() {0, 0, 0, 1, 0}, New Single() {1, 1, 1, 0, 1}})
' create some image attributes
Dim attributes As New ImageAttributes()
attributes.SetColorMatrix(colorMatrix)
g.DrawImage(source, New Rectangle(0, 0, source.Width, source.Height), 0, 0, source.Width, source.Height, _
GraphicsUnit.Pixel, attributes)
'dispose the Graphics object
g.Dispose()
Return newBitmap
End Function
You aren't setting Matrix33 or Matrix44. My understanding is that Matrix33 would be the alpha component, so I suspect you're making your entire image transparent.
Try setting Matrix33 = 1.
I used "SIMD Supported Vectors" to make a code faster then pointers.Its under System.Numerics namespace.But before using these classes you need to get the update for System.Numerics from NuGet.Just search for System.Numerics.Anyways thats my class to invert the image
public class VBitmap : IDisposable
{
public short Width { get; private set; }
public short Height { get; private set; }
public int Stride { get; private set; }
public int PixelLenght { get; private set; }
public byte BytesperPixel { get; private set; }
public PixelFormat PixelFormat { get; private set; }
public byte[] Pixels { get; private set; }
public VBitmap(string path)
{
using (Bitmap bmp = new Bitmap(path))
{
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
Width = checked((short)bmp.Width);
Height = checked((short)bmp.Height);
Stride = bmpdata.Stride;
PixelLenght = Stride * Height;
BytesperPixel = checked((byte)(Stride / Width));
PixelLenght = (PixelLenght % 16) != 0 ? PixelLenght + (PixelLenght % 16) : PixelLenght;
PixelFormat = bmp.PixelFormat;
Pixels = new byte[PixelLenght];
Marshal.Copy(bmpdata.Scan0, Pixels, 0, PixelLenght);
bmp.UnlockBits(bmpdata);
}
}
~VBitmap()
{
Dispose();
}
public void InvertPixels()
{
byte[] resultarray = Pixels;
unsafe
{
fixed (byte* result_ptr = resultarray)
{
for (int i = 0; i < PixelLenght; i += 16)
(~new Vector<byte>(Pixels, i)).CopyTo(resultarray, i);
}
}
}
public void InvertPixels(string name)
{
byte[] resultarray = Pixels;
unsafe
{
fixed (byte* result_ptr = resultarray)
{
for (int i = 0; i < PixelLenght; i += 16)
(~new Vector<byte>(Pixels, i)).CopyTo(resultarray, i);
SaveImage(name);
}
}
}
public unsafe void SaveImage(string name)
{
fixed (byte* p_ptr = Pixels)
{
using (Bitmap resultbmp = new Bitmap(Width, Height, Stride, PixelFormat, (IntPtr)p_ptr))
{
resultbmp.Save(name, ImageFormat.Jpeg);
}
}
}
public void Dispose()
{
Width = 0;
Height = 0;
Stride = 0;
PixelLenght = 0;
BytesperPixel = 0;
Pixels = null;
GC.Collect();
}
}
Usage.Note:To get best performance from vectors you need to run your code on Release Mode:
static void Main(string[] args)
{
new VBitmap("testp.png").InvertPixels("qq.jpg");//Inverts the given bitmap and saves it.
}
By inverting, do you mean creating a negative? If yes here is a snippet:
public void ApplyInvert()
{
byte A, R, G, B;
Color pixelColor;
for (int y = 0; y < bitmapImage.Height; y++)
{
for (int x = 0; x < bitmapImage.Width; x++)
{
pixelColor = bitmapImage.GetPixel(x, y);
A = pixelColor.A;
R = (byte)(255 - pixelColor.R);
G = (byte)(255 - pixelColor.G);
B = (byte)(255 - pixelColor.B);
bitmapImage.SetPixel(x, y, Color.FromArgb((int)A, (int)R, (int)G, (int)B));
}
}
}
from: http://www.smokycogs.com/blog/image-processing-in-c-sharp-inverting-an-image/
If you want read more about your problem and color matrix, please proceed with the following link: https://web.archive.org/web/20141230042200/http://bobpowell.net/negativeimage.aspx
var gfxPicture = Graphics.FromImage(img);
==>
var gfxPicture = Graphics.FromImage(bmpPicture);
DrawImage could distort image, GetPixel is slow, Try WPF imaging API

cropping an area from BitmapData with C#

I have a bitmap sourceImage.bmp
locking it's bits:
BitmapData dataOriginal = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Do analysis, get a clone:
Bitmap originalClone = AForge.Imaging.Image.Clone(dataOriginal);
unlocking bits:
sourceImage.UnlockBits(dataOriginal);
is it possible to specify which part of "dataOriginal" to copy (x,y,w,h)? or to create new data from the dataOriginal, specifying X and Y coordinates as well as H and W?
The aim is to copy a small area from this image. This method might be faster than DrawImage, that's why I don't use the latter.
Edit:
So I took 29 Mb bitmap and did some hardcore testing! Full-size crop (basically a copy) + 100 iterations.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AForge;
using AForge.Imaging;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
namespace testCropClone
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private unsafe Bitmap Clone(Bitmap bmp, int startX, int startY, int width, int height)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int origByteCount = rawOriginal.Stride * rawOriginal.Height;
byte[] origBytes = new Byte[origByteCount];
Marshal.Copy(rawOriginal.Scan0, origBytes, 0, origByteCount);
int BPP = 4; //4 Bpp = 32 bits, 3 = 24, etc.
byte[] croppedBytes = new Byte[width * height * BPP];
//Iterate the selected area of the original image, and the full area of the new image
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width * BPP; j += BPP)
{
int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j);
int croppedIndex = (i * width * BPP) + (j);
//copy data: once for each channel
for (int k = 0; k < BPP; k++)
{
croppedBytes[croppedIndex + k] = origBytes[origIndex + k];
}
}
}
//copy new data into a bitmap
Bitmap croppedBitmap = new Bitmap(width, height);
BitmapData croppedData = croppedBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(croppedBytes, 0, croppedData.Scan0, croppedBytes.Length);
bmp.UnlockBits(rawOriginal);
croppedBitmap.UnlockBits(croppedData);
return croppedBitmap;
}
private Bitmap cloneBitmap(Bitmap bmp, int startX, int startY, int width, int height)
{
Rectangle srcRect = Rectangle.FromLTRB(startX, startY, width, height);
Bitmap cloneBitmap = bmp.Clone(srcRect, bmp.PixelFormat);
return cloneBitmap;
}
private Bitmap cloneRectangle(Bitmap bmp, int startX, int startY, int width, int height)
{
Rectangle srcRect = Rectangle.FromLTRB(startX, startY, width, height);
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
private Bitmap cloneAforge(Bitmap bmp, int startX, int startY, int width, int height)
{
BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Bitmap cloneBitmap = AForge.Imaging.Image.Clone(rawOriginal);
bmp.UnlockBits(rawOriginal);
return cloneBitmap;
}
private void button1_Click(object sender, EventArgs e)
{
Bitmap source = new Bitmap(#"C:\9\01.bmp");
Stopwatch s1 = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
Bitmap Clone1 = cloneAforge(source, 0, 0, source.Width, source.Height);
Clone1.Dispose();
}
/*Bitmap Clone1 = cloneAforge(source, 0, 0, source.Width, source.Height);
Clone1.Save(#"C:\9\01_aforge.bmp");
Clone1.Dispose();*/
s1.Stop();
source.Dispose();
textBox1.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms");
}
private void button2_Click(object sender, EventArgs e)
{
Bitmap source = new Bitmap(#"C:\9\01.bmp");
Stopwatch s1 = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
Bitmap Clone1 = cloneBitmap(source, 0, 0, source.Width, source.Height);
Clone1.Dispose();
}
/*Bitmap Clone1 = cloneBitmap(source, 0, 0, source.Width, source.Height);
Clone1.Save(#"C:\9\01_bitmap.bmp");
Clone1.Dispose();*/
s1.Stop();
source.Dispose();
textBox2.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms");
}
private void button3_Click(object sender, EventArgs e)
{
Bitmap source = new Bitmap(#"C:\9\01.bmp");
Stopwatch s1 = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
Bitmap Clone1 = Clone(source, 0, 0, source.Width, source.Height);
Clone1.Dispose();
}
/*Bitmap Clone1 = Clone(source, 0, 0, source.Width, source.Height);
Clone1.Save(#"C:\9\01_bits.bmp");
Clone1.Dispose();*/
s1.Stop();
source.Dispose();
textBox3.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms");
}
private void button4_Click(object sender, EventArgs e)
{
Bitmap source = new Bitmap(#"C:\9\01.bmp");
Stopwatch s1 = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
Bitmap Clone1 = cloneRectangle(source, 0, 0, source.Width, source.Height);
Clone1.Dispose();
}
/*Bitmap Clone1 = cloneRectangle(source, 0, 0, source.Width, source.Height);
Clone1.Save(#"C:\9\01_rect.bmp");
Clone1.Dispose();*/
s1.Stop();
source.Dispose();
textBox4.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms");
}
}
}
Edit2: (Aforge full-size Crop..) method Nr. 2
for (int i = 0; i < 100; i++)
{
Crop crop = new Crop(new Rectangle(0, 0, source.Width, source.Height));
var source2 = crop.Apply(source);
source2.Dispose();
}
Average = 62ms (40ms less that 1st Aforge approach)
Results:
BitmapClone (0 ms) ?? (cheating, isn't it?)
Aforge #2 (65 ms)
Aforge #1 (105 ms)
Rectangle (170 ms)
Lock Bits (803 ms) (waiting for fixes/new test results..)
I whipped up a quick (and admittedly rough) manual solution that demonstrates how to do this using locked bitmaps. It should be considerably faster than the alternative methods, but does involve a lot more code.
Bitmap bmp = new Bitmap(#"C:\original.jpg");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int origByteCount = rawOriginal.Stride * rawOriginal.Height;
byte[] origBytes = new Byte[origByteCount];
Marshal.Copy(rawOriginal.Scan0, origBytes, 0, origByteCount);
//I want to crop a 100x100 section starting at 15, 15.
int startX = 15;
int startY = 15;
int width = 100;
int height = 100;
int BPP = 4; //4 Bpp = 32 bits, 3 = 24, etc.
byte[] croppedBytes = new Byte[width * height * BPP];
//Iterate the selected area of the original image, and the full area of the new image
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width * BPP; j += BPP)
{
int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j);
int croppedIndex = (i * width * BPP) + (j);
//copy data: once for each channel
for (int k = 0; k < BPP; k++)
{
croppedBytes[croppedIndex + k] = origBytes[origIndex + k];
}
}
}
//copy new data into a bitmap
Bitmap croppedBitmap = new Bitmap(width, height);
BitmapData croppedData = croppedBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(croppedBytes, 0, croppedData.Scan0, croppedBytes.Length);
bmp.UnlockBits(rawOriginal);
croppedBitmap.UnlockBits(croppedData);
croppedBitmap.Save(#"C:\test.bmp");
I used this original image:
To output this image, cropped to 100x100 # 15,15:
Obviously if you use this code, you'll want to clean it up a bit and add error handling. If I understand your question correctly, doing things this way should eliminate the need to use AForge at all.
Fopedush's answer benefits greatly when we subsitute Marshal.copy with memcpy, because that way we don't have to copy it through a byte[] array. That way the memory gets copied only once, instead of three times!
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static unsafe extern int memcpy(byte* dest, byte* src, long count);
static public Bitmap cropBitmap(Bitmap sourceImage, Rectangle rectangle)
{
const int BPP = 4; //4 Bpp = 32 bits; argb
var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppArgb);
var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
unsafe
{
croppedBitmapData.Stride = sourceBitmapdata.Stride;
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
memcpy(croppedImagePointer, sourceImagePointer,
Math.Abs(croppedBitmapData.Stride) * rectangle.Height);
}
sourceImage.UnlockBits(sourceBitmapdata);
croppedImage.UnlockBits(croppedBitmapData);
return croppedImage;
}
My results are:
BitmapClone: 1823 ms
LockBits: 4857 ms
Rectangle: 1479 ms
My method: 559 ms
My method with LockBits on source image done only once (before loop): 160 ms
I don't have AForge so I haven't included that, but by looking on op's results it would be slower than this. I was testing cropping the image in half.
Please note, that if we would exchange memcpy with:
for (int k = 0; k < Math.Abs(croppedBitmapData.Stride) * rectangle.Height; k++)
*(croppedImagePointer++) = *(sourceImagePointer++);
it gets 10x slower!
You can try something like this:
public static Bitmap CropBitmap(Bitmap bitmap, int x, int y, int w, int h)
{
Rectangle rect = new Rectangle(x, y, w, h);
Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat);
return cropped;
}
And do something like this in yout code (sample):
var croppedImagem = CropBitmap(dataOriginal, 0, 0, 100, 100);
I hope it helps!
I am a new user and can't vote yet, otherwise I would have upvoted Korwin80's answer as it provides the most efficient working solution, in my opinion. trakos' solution may execute faster but yields scrambled images, at least for me. Here is how I applied Korwin80's solution, with some minor improvements, in my own code:
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private unsafe static extern int memcpy(byte* dest, byte* src, long count);
private unsafe Bitmap Crop(Bitmap srcImg, Rectangle rectangle)
{
if ((srcImg.Width == rectangle.Width) && (srcImg.Height == rectangle.Height))
return srcImg;
var srcImgBitmapData = srcImg.LockBits(new Rectangle(0, 0, srcImg.Width, srcImg.Height), ImageLockMode.ReadOnly, srcImg.PixelFormat);
var bpp = srcImgBitmapData.Stride / srcImgBitmapData.Width; // 3 or 4
var srcPtr = (byte*)srcImgBitmapData.Scan0.ToPointer() + rectangle.Y * srcImgBitmapData.Stride + rectangle.X * bpp;
var srcStride = srcImgBitmapData.Stride;
var dstImg = new Bitmap(rectangle.Width, rectangle.Height, srcImg.PixelFormat);
var dstImgBitmapData = dstImg.LockBits(new Rectangle(0, 0, dstImg.Width, dstImg.Height), ImageLockMode.WriteOnly, dstImg.PixelFormat);
var dstPtr = (byte*)dstImgBitmapData.Scan0.ToPointer();
var dstStride = dstImgBitmapData.Stride;
for (int y = 0; y < rectangle.Height; y++)
{
memcpy(dstPtr, srcPtr, dstStride);
srcPtr += srcStride;
dstPtr += dstStride;
}
srcImg.UnlockBits(srcImgBitmapData);
dstImg.UnlockBits(dstImgBitmapData);
return dstImg;
}
this class gets your bitmap obj . then lockbits. in ctor.
When you call crop method, it uses memcpy to copy the desired region to new bmp.
lockbits: tells the garbage collector to NOT move my bits anywhere, cuz im gonna modify it by pointers (scan0).
memcpy : fastest copy. can copy memory blocks. optimized by some experts.
why memcpy fast?
instead of copying byte by byte, (widthheight) times memory access .
memcpy does it block by block, much more less than wh times .
internal unsafe sealed class FastImageCroper : IDisposable
{
private readonly Bitmap _srcImg;
private readonly BitmapData _srcImgBitmapData;
private readonly int _bpp;
private readonly byte* _srtPrt;
public FastImageCroper(Bitmap srcImg)
{
_srcImg = srcImg;
_srcImgBitmapData = srcImg.LockBits(new Rectangle(0, 0, srcImg.Width, srcImg.Height), ImageLockMode.ReadOnly, srcImg.PixelFormat);
_bpp = _srcImgBitmapData.Stride / _srcImgBitmapData.Width; // == 4
_srtPrt = (byte*)_srcImgBitmapData.Scan0.ToPointer();
}
public Bitmap Crop(Rectangle rectangle)
{
Bitmap dstImg = new Bitmap(rectangle.Width, rectangle.Height, _srcImg.PixelFormat);
BitmapData dstImgBitmapData = dstImg.LockBits(new Rectangle(0, 0, dstImg.Width, dstImg.Height), ImageLockMode.WriteOnly, dstImg.PixelFormat);
byte* dstPrt = (byte*)dstImgBitmapData.Scan0.ToPointer();
byte* srcPrt = _srtPrt + rectangle.Y*_srcImgBitmapData.Stride + rectangle.X*_bpp;
for (int y = 0; y < rectangle.Height; y++)
{
int srcIndex = y * _srcImgBitmapData.Stride;
int croppedIndex = y * dstImgBitmapData.Stride;
memcpy(dstPrt + croppedIndex, srcPrt + srcIndex, dstImgBitmapData.Stride);
}
dstImg.UnlockBits(dstImgBitmapData);
return dstImg;
}
public void Dispose()
{
_srcImg.UnlockBits(_srcImgBitmapData);
}
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcpy(byte* dest, byte* src, long count);
}

Effective way of making negative of image without external dlls

That is the solution to make a negative from a image in C# Windows Forms without any dlls and in a effective, fast way?
The best way to do this is directly accessing the pixels with bitmap data.
Just to add some timing details:
Performing Negate on an 8 Megapixel Image (on a 2.4 Ghz Core 2 Duo):
SetPixel (~22 seconds) - 220 Times slower
Color Matrix, Matajon's method below (~750 milliseconds) - 7 times slower
Directly accesing the bitmap data (~100 milliseconds) - Fastest
So, if you can't have unsafe code, then Color Matrix is much better than SetPixel.
public static void Negate(Bitmap image)
{
const int RED_PIXEL = 2;
const int GREEN_PIXEL = 1;
const int BLUE_PIXEL = 0;
BitmapData bmData = currentImage.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat);
try
{
int stride = bmData.Stride;
int bytesPerPixel = (currentImage.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4);
unsafe
{
byte* pixel = (byte*)(void*)bmData.Scan0;
int yMax = image.Height;
int xMax = image.Width;
for (int y = 0; y < yMax; y++)
{
int yPos = y * stride;
for (int x = areaSize.X; x < xMax; x++)
{
int pos = yPos + (x * bytesPerPixel);
pixel[pos + RED_PIXEL] = (byte)(255 - pixel[pos + RED_PIXEL]);
pixel[pos + GREEN_PIXEL] = (byte)(255 - pixel[pos + GREEN_PIXEL]);
pixel[pos + BLUE_PIXEL] = (byte)(255 - pixel[pos + BLUE_PIXEL]);
}
}
}
}
finally
{
image.UnlockBits(bmData);
}
}
If you are interested, here is the code for Color Matrix:
public static void Negate(Bitmap image)
{
Bitmap clone = (Bitmap) image.Clone();
using (Graphics g = Graphics.FromImage(image))
{
// negation ColorMatrix
ColorMatrix colorMatrix = new ColorMatrix(
new float[][]
{
new float[] {-1, 0, 0, 0, 0},
new float[] {0, -1, 0, 0, 0},
new float[] {0, 0, -1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(colorMatrix);
g.DrawImage(clone, new Rectangle(0, 0, clone.Width, clone.Height),
0, 0, clone.Width, clone.Height, GraphicsUnit.Pixel, attributes);
}
}
Go through all the pixels one by one (Bitmap.GetPixel() or something) and subtract the RGB values from 0xff to create a pixel of negative color. Save this pixel to a new image or onto the same image using (Bitmap.SetPixel()) at the same position.
// Retrieve the image.
var image1 = new Bitmap(#"C:\Documents and Settings\All Users\"
+ #"Documents\My Music\music.bmp", true);
int x, y;
// Loop through the images pixels to reset color.
for(x=0; x<image1.Width; x++)
{
for(y=0; y<image1.Height; y++)
{
Color pixelColor = image1.GetPixel(x, y);
Color newColor = Color.FromArgb(0xff - pixelColor.R
, 0xff - pixelColor.G, 0xff - pixelColor.B);
image1.SetPixel(x, y, newColor);
}
}

Categories