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.
Related
Using AForge.Video.FFMPEG I am able to create video from Images.
string[] files;
string folderPath = #"FolderPath";
files = Directory.GetFiles(folderPath).OrderBy(c => c).ToArray();
VideoFileWriter writer = new VideoFileWriter();
writer.Open(#"C:folder\new.mp4", imageWidth, imageHeight, 10, VideoCodec.MPEG4);
for (int j = 0; j < files.Length; j++)
{
string fileName = files[j];
BitmapSource imageBitMap = new BitmapImage(new Uri(fileName, UriKind.RelativeOrAbsolute));
imageBitMap.Freeze();
int stride = imageBitMap.PixelWidth * ((imageBitMap.Format.BitsPerPixel + 7) / 8);
byte[] ImageInBits = new byte[imageBitMap.PixelWidth * imageBitMap.PixelHeight];
imageBitMap.CopyPixels(ImageInBits, stride, 0);
Bitmap image = new Bitmap(imageWidth, imageHeight, stride, PixelFormat.Format8bppIndexed, Marshal.UnsafeAddrOfPinnedArrayElement(ImageInBits, 0));
writer.WriteVideoFrame(image);
image.Dispose();
}
I am trying to add text/String to input image using
private Bitmap WriteString(Bitmap bmp)
{
RectangleF rectf = new RectangleF(70, 90, 90, 50);
Bitmap tempBitmap = new Bitmap(bmp.Width, bmp.Height);
Graphics g = Graphics.FromImage(tempBitmap);
g.DrawImage(bmp, 0, 0);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawString("yourText", new Font("Tahoma", 8), Brushes.Red, rectf);
g.Flush();
return bmp;
}
like
Bitmap image = new Bitmap(imageWidth, imageHeight, stride, PixelFormat.Format8bppIndexed, Marshal.UnsafeAddrOfPinnedArrayElement(ImageInBits, 0));
Bitmap outputBitmap=WriteString(image);
writer.WriteVideoFrame(outputBitmap);
How can I write a string to Image?
Is there any way to add subtitle to AForge.Video.FFMEG to image before creating video?
private Bitmap WriteString(Bitmap bmp) {
Bitmap tempBitmap = new Bitmap(bmp.Width, bmp.Height); << create new
..draw on copy..
return bmp; <<< return original
}
Seems to be the wrong.
I'm trying to dynamically change my cursor to a rectangle that is translucent. I have everything worked out except the color of transparency is too pale. It's almost like I need to boost the saturation. I want a high intensity yellow that is translucent. If I increase the matrix33 float toward 1.0 it becomes more opaque. Any ideas how to accomplish this?
Here's my current (nearly) working code.
private Cursor RecCursor(int x, int y)
{
//Cursor CustomCursor = RecCursor(200, 200);
Cursor c = null;
try
{
var pic = new Bitmap(x, y);
Graphics gr = Graphics.FromImage(pic);
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create location and size of rectangle.
float a = 0.0F;
float b = 0.0F;
float width = x;
float height = y;
Color tColor = Color.FromArgb(255, 255, 255, 0);
SolidBrush brush = new SolidBrush(tColor);
//create a Bitmap the size of the image provided
Bitmap bmp = new Bitmap(pic.Width, pic.Height);
//create a graphics object from the image
using (Graphics gfx = Graphics.FromImage(bmp))
{
//create a color matrix object
ColorMatrix matrix = new ColorMatrix();
//set the opacity
matrix.Matrix33 = 0.5f;
//create image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color(opacity) of the image
attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Default);
gr.FillRectangle(brush, a, b, width, height);
gr.DrawRectangle(blackPen, a, b, width, height);
//now draw the image
gfx.DrawImage(pic, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, pic.Width, pic.Height, GraphicsUnit.Pixel, attributes);
}
IntPtr ptr = bmp.GetHicon();
Bitmap bitonalBMP = new Bitmap((int)width, (int)height, PixelFormat.Format1bppIndexed);
//This changes the hotspot of the cursor
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.hbmColor = bmp.GetHbitmap();
tmp.hbmMask = bitonalBMP.GetHbitmap();
tmp.xHotspot = 0;
tmp.yHotspot = 0;
tmp.fIcon = false;
ptr = CreateIconIndirect(ref tmp);
c = new Cursor(ptr);
//if (tmp.hbmColor != IntPtr.Zero) DeleteObject(tmp.hbmColor);
//if (tmp.hbmMask != IntPtr.Zero) DeleteObject(tmp.hbmMask);
//if (ptr != IntPtr.Zero) DestroyIcon(ptr);
pic.Dispose();
gr.Dispose();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return c;
}
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
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);
}
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();