I am extracting info from about 70,000 images and wanted to get their average RGB info. The current code below works, but runs out of memory. So far it just prints out the camera make, model, RGB values and file name. To get started, I found this code, which I adapted:
public static RGBValues AverageRGB(Bitmap bm)
{
BitmapData srcData = bm.LockBits(
new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
System.Drawing.Imaging.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];
}
}
}
}
long avgB = totals[0] / (width * height);
long avgG = totals[1] / (width * height);
long avgR = totals[2] / (width * height);
RGBValues rgb = new RGBValues();
rgb.R = avgR;
rgb.G = avgG;
rgb.B = avgB;
return rgb;
}
public class RGBValues
{
public long R;
public long G;
public long B;
}
I then call it like this:
public MainWindow()
{
string folder = #"F:\Photos";
DirSearch(folder);
int counter = 0;
foreach (var filename in filelist)
{
GetImageInfo(filename);
counter++;
if (counter >= 10)
{
GC.Collect();
GC.WaitForPendingFinalizers();
counter = 0;
}
}
InitializeComponent();
}
private void GetImageInfo(string filename)
{
FileInfo info = new FileInfo(filename);
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Bitmap bmp = new Bitmap(filename);
PropertyItem[] propItems = bmp.PropertyItems;
var temp = AverageRGB(bmp);
string manufacturer = encoding.GetString(propItems[1].Value);
string model = encoding.GetString(propItems[2].Value);
Console.WriteLine("manufacturer: " + manufacturer + ", model: " + model + ", colorinfo: " + temp.R + ", " + temp.G + ", " + temp.B + ", Filename: " + info.Name);
}
public static void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d))
{
filelist.Add(f);
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
I threw in some garbage collection efforts, but it still runs out of memory after doing around 150 photos. I thought that C# would automatically discard info it didn't need anymore. Is it because this is using unsafe that it continues to hold on to extra info? What can I do to force it to purge old info?
Related
Store an image with the ".raw" extension in a two-dimensional byte array. Convert it to bitmap. I want to show this in the picture box, but if I run it with the code below, I get an error that the parameter is wrong.
Width and height are obtained from the information provided by the header file.
I wonder what I'm doing wrong.
string filename = #"test.raw";
byte[] rawBytes = File.ReadAllBytes(filename);
int bytePixel = 2;
int width = samples*bytePixel;
int height = lines;
byte[,] rawData = new byte[height, width];
int counter = new int();
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++, counter++)
{
rawData[i, j] = rawBytes[counter];
}
}
Bitmap bitmapImage = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);
BitmapData bitmapImageData = bitmapImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormap.Format16bppGrayScale);
unsafe
{
byte* pointer = (byte*)bitmapImageData.Scan0.ToPointer();
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++, pointer++)
{
*pointer = rawData[y, x];
}
}
}
bitmapImage.UnlockBits(bitmapImageData);
pictureBox1.Image = bitmapImage;
Please give me some advice.
I can't figure out what's wrong but if you just want to see byte array result on screen, this func will make bmp file with IntPtr. Hope it helps.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var fs = new FileStream("test.bmp", FileMode.Open);
Bitmap bitmap = new Bitmap(800, 600, PixelFormat.Format16bppGrayScale);
BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, 800, 600), ImageLockMode.WriteOnly, PixelFormat.Format16bppGrayScale);
unsafe
{
byte* p = (byte*)bitmapdata.Scan0.ToPointer();
for (int i = 0; i < 600; i++)
{
for (int j = 0; j < 800; j++)
{
*p = (byte)(i * j); p++;
}
}
}
FileSaveBMP($"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.bmp", bitmapdata.Scan0, new CRect() { Width = 800, Height = 600 }, 800);
bitmap.UnlockBits(bitmapdata);
//pictureBox1.Image = bitmap;
}
private unsafe void FileSaveBMP(string sFile, IntPtr ptr, CRect rect, int w, int p_nByte = 1)
{
FileStream fs = new FileStream(sFile, FileMode.Create, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(Convert.ToUInt16(0x4d42));
if (p_nByte == 1)
{
if ((Int64)rect.Width * (Int64)rect.Height > Int32.MaxValue) bw.Write(Convert.ToUInt32(54 + 1024 + p_nByte * 1000 * 1000));
else bw.Write(Convert.ToUInt32(54 + 1024 + p_nByte * (Int64)rect.Width * (Int64)rect.Height));
}
else if (p_nByte == 3)
{
if ((Int64)rect.Width * (Int64)rect.Height > Int32.MaxValue) bw.Write(Convert.ToUInt32(54 + p_nByte * 1000 * 1000));//uint bfSize = br.ReadUInt32();
else bw.Write(Convert.ToUInt32(54 + p_nByte * (Int64)rect.Width * (Int64)rect.Height));//uint bfSize = br.ReadUInt32();
}
//image 크기 bw.Write(); bmfh.bfSize = sizeof(14byte) + nSizeHdr + rect.right * rect.bottom;
bw.Write(Convert.ToUInt16(0)); //reserved // br.ReadUInt16();
bw.Write(Convert.ToUInt16(0)); //reserved //br.ReadUInt16();
if (p_nByte == 1)
bw.Write(Convert.ToUInt32(1078));
else if (p_nByte == 3)
bw.Write(Convert.ToUInt32(54));//uint bfOffBits = br.ReadUInt32();
bw.Write(Convert.ToUInt32(40));// uint biSize = br.ReadUInt32();
bw.Write(Convert.ToInt32(rect.Width));// nWidth = br.ReadInt32();
bw.Write(Convert.ToInt32(rect.Height));// nHeight = br.ReadInt32();
bw.Write(Convert.ToUInt16(1));// a = br.ReadUInt16();
bw.Write(Convert.ToUInt16(8 * p_nByte)); //byte // nByte = br.ReadUInt16() / 8;
bw.Write(Convert.ToUInt32(0)); //compress //b = br.ReadUInt32();
if ((Int64)rect.Width * (Int64)rect.Height > Int32.MaxValue) bw.Write(Convert.ToUInt32(1000 * 1000));// b = br.ReadUInt32();
else bw.Write(Convert.ToUInt32((Int64)rect.Width * (Int64)rect.Height));// b = br.ReadUInt32();
bw.Write(Convert.ToInt32(0));//a = br.ReadInt32();
bw.Write(Convert.ToInt32(0));// a = br.ReadInt32();
bw.Write(Convert.ToUInt32(256)); //color //b = br.ReadUInt32();
bw.Write(Convert.ToUInt32(256)); //import // b = br.ReadUInt32();
if (p_nByte == 1)
{
for (int i = 0; i < 256; i++)
{
bw.Write(Convert.ToByte(i));
bw.Write(Convert.ToByte(i));
bw.Write(Convert.ToByte(i));
bw.Write(Convert.ToByte(255));
}
}
if (rect.Width % 4 != 0)
{
rect.Right += 4 - rect.Width % 4;
}
byte[] aBuf = new byte[p_nByte * rect.Width];
for (int i = rect.Height - 1; i >= 0; i--)
{
Marshal.Copy((IntPtr)((long)ptr + rect.Left + ((long)i + (long)rect.Top) * w * p_nByte), aBuf, 0, rect.Width * p_nByte);
bw.Write(aBuf);
}
bw.Close();
fs.Close();
}
}
public class CRect
{
public int Left
{
get; set;
}
public int Right
{
get; set;
}
public int Top
{
get; set;
}
public int Bottom
{
get; set;
}
public int Width
{
get; set;
}
public int Height
{
get; set;
}
}
Above code creates image file like this.
In the following source code, I am trying to do the following:
Obtain a 3x3 array of double values
Convert that double array to Bitmap
Pad that Bitmap.
Bitmap image = ImageDataConverter.ToBitmap(new double[,]
{
{ .11, .11, .11, },
{ .11, .11, .11, },
{ .11, .11, .11, },
});
Bitmap paddedBitmap = ImagePadder.Pad(image, 512, 512);
pictureBox1.Image = paddedBitmap;
But, this source code is generating the following exception in the BitmapLocker.GetPixel(), because, i = 8, and dataLength = 7.
Please, note that, image-stride is always found to be 4, no matter what the size of the dimensions are.
How can I fix this?
.
Relevant Source Code
ImageDataConverter.cs
public class ImageDataConverter
{
public static Bitmap ToBitmap(double[,] input)
{
int width = input.GetLength(0);
int height = input.GetLength(1);
Bitmap output = Grayscale.CreateGrayscaleImage(width, height);
BitmapData data = output.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly,
output.PixelFormat);
int pixelSize = System.Drawing.Image.GetPixelFormatSize(output.PixelFormat) / 8;
int offset = data.Stride - width * pixelSize;
double Min = 0.0;
double Max = 255.0;
unsafe
{
byte* address = (byte*)data.Scan0.ToPointer();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
double v = 255 * (input[x, y] - Min) / (Max - Min);
byte value = unchecked((byte)v);
for (int c = 0; c < pixelSize; c++, address++)
{
*address = value;
}
}
address += offset;
}
}
output.UnlockBits(data);
return output;
}
}
ImagePadder.cs
public class ImagePadder
{
public static Bitmap Pad(Bitmap image, int newWidth, int newHeight)
{
int width = image.Width;
int height = image.Height;
if (width >= newWidth) throw new Exception("New width must be larger than the old width");
if (height >= newHeight) throw new Exception("New height must be larger than the old height");
Bitmap paddedImage = Grayscale.CreateGrayscaleImage(newWidth, newHeight);
BitmapLocker inputImageLocker = new BitmapLocker(image);
BitmapLocker paddedImageLocker = new BitmapLocker(paddedImage);
inputImageLocker.Lock();
paddedImageLocker.Lock();
//Reading row by row
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++)
{
Color col = inputImageLocker.GetPixel(x, y);
paddedImageLocker.SetPixel(x, y, col);
}
}
string str = string.Empty;
paddedImageLocker.Unlock();
inputImageLocker.Unlock();
return paddedImage;
}
}
BitmapLocker.cs
public class BitmapLocker : IDisposable
{
//private properties
Bitmap _bitmap = null;
BitmapData _bitmapData = null;
private byte[] _imageData = null;
//public properties
public bool IsLocked { get; set; }
public IntPtr IntegerPointer { get; private set; }
public int Width { get { return _bitmap.Width; } }
public int Height { get { return _bitmap.Height; } }
public int Stride { get { return _bitmapData.Stride; } }
public int ColorDepth { get { return Bitmap.GetPixelFormatSize(_bitmap.PixelFormat); } }
public int Channels { get { return ColorDepth / 8; } }
public int PaddingOffset { get { return _bitmapData.Stride - (_bitmap.Width * Channels); } }
public PixelFormat ImagePixelFormat { get { return _bitmap.PixelFormat; } }
public bool IsGrayscale { get { return Grayscale.IsGrayscale(_bitmap); } }
//Constructor
public BitmapLocker(Bitmap source)
{
IsLocked = false;
IntegerPointer = IntPtr.Zero;
this._bitmap = source;
}
/// Lock bitmap
public void Lock()
{
if (IsLocked == false)
{
try
{
// Lock bitmap (so that no movement of data by .NET framework) and return bitmap data
_bitmapData = _bitmap.LockBits(
new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
ImageLockMode.ReadWrite,
_bitmap.PixelFormat);
// Create byte array to copy pixel values
int noOfBitsNeededForStorage = _bitmapData.Stride * _bitmapData.Height;
int noOfBytesNeededForStorage = noOfBitsNeededForStorage / 8;
_imageData = new byte[noOfBytesNeededForStorage * ColorDepth];//# of bytes needed for storage
IntegerPointer = _bitmapData.Scan0;
// Copy data from IntegerPointer to _imageData
Marshal.Copy(IntegerPointer, _imageData, 0, _imageData.Length);
IsLocked = true;
}
catch (Exception)
{
throw;
}
}
else
{
throw new Exception("Bitmap is already locked.");
}
}
/// Unlock bitmap
public void Unlock()
{
if (IsLocked == true)
{
try
{
// Copy data from _imageData to IntegerPointer
Marshal.Copy(_imageData, 0, IntegerPointer, _imageData.Length);
// Unlock bitmap data
_bitmap.UnlockBits(_bitmapData);
IsLocked = false;
}
catch (Exception)
{
throw;
}
}
else
{
throw new Exception("Bitmap is not locked.");
}
}
public Color GetPixel(int x, int y)
{
Color clr = Color.Empty;
// Get color components count
int cCount = ColorDepth / 8;
// Get start index of the specified pixel
int i = (Height - y - 1) * Stride + x * cCount;
int dataLength = _imageData.Length - cCount;
if (i > dataLength)
{
throw new IndexOutOfRangeException();
}
if (ColorDepth == 32) // For 32 bpp get Red, Green, Blue and Alpha
{
byte b = _imageData[i];
byte g = _imageData[i + 1];
byte r = _imageData[i + 2];
byte a = _imageData[i + 3]; // a
clr = Color.FromArgb(a, r, g, b);
}
if (ColorDepth == 24) // For 24 bpp get Red, Green and Blue
{
byte b = _imageData[i];
byte g = _imageData[i + 1];
byte r = _imageData[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (ColorDepth == 8)
// For 8 bpp get color value (Red, Green and Blue values are the same)
{
byte c = _imageData[i];
clr = Color.FromArgb(c, c, c);
}
return clr;
}
public void SetPixel(int x, int y, Color color)
{
// Get color components count
int cCount = ColorDepth / 8;
// Get start index of the specified pixel
int i = (Height - y - 1) * Stride + x * cCount;
try
{
if (ColorDepth == 32) // For 32 bpp set Red, Green, Blue and Alpha
{
_imageData[i] = color.B;
_imageData[i + 1] = color.G;
_imageData[i + 2] = color.R;
_imageData[i + 3] = color.A;
}
if (ColorDepth == 24) // For 24 bpp set Red, Green and Blue
{
_imageData[i] = color.B;
_imageData[i + 1] = color.G;
_imageData[i + 2] = color.R;
}
if (ColorDepth == 8)
// For 8 bpp set color value (Red, Green and Blue values are the same)
{
_imageData[i] = color.B;
}
}
catch (Exception ex)
{
throw new Exception("(" + x + ", " + y + "), " + _imageData.Length + ", " + ex.Message + ", i=" + i);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
_bitmap = null;
_bitmapData = null;
_imageData = null;
IntegerPointer = IntPtr.Zero;
}
}
}
The problem is in BitmapLocker class. Besides obvious inefficiencies, the class contains two serious bugs.
The first (which is causing the exception) is the incorrect buffer size calculation inside Lock method:
int noOfBitsNeededForStorage = _bitmapData.Stride * _bitmapData.Height;
int noOfBytesNeededForStorage = noOfBitsNeededForStorage / 8;
_imageData = new byte[noOfBytesNeededForStorage * ColorDepth];//# of bytes needed for storage
The Stride property returns
The stride width, in bytes, of the Bitmap object.
and also
The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
so the correct calculation (shown in several LockBits related MSDN samples) is:
int noOfBytesNeededForStorage = Math.Abs(_bitmapData.Stride) * _bitmapData.Height;
_imageData = new byte[noOfBytesNeededForStorage];
which will fix the exception (your code was doing (12 / 8) * 8 which resulted in 8 rather than the expected 12).
The second issue is the determination of the start index of the specified pixel here:
int i = (Height - y - 1) * Stride + x * cCount;
which is calculation for bottom-up bitmap with positive Stride, which as you can see from the documentation is not possible.
Hence the correct calculation should be something like this:
int i = (Stride > 0 ? y * Stride : (Height - y - 1) * -Stride) + x * cCount;
or
int i = (Stride > 0 ? y : y - Height + 1) * Stride + x * cCount;
This should be changed in both GetPixel and SetPixel methods.
I found some great code here on SO and have implemented it in my app and although it is "working", the blur effect is not strong enough to actually realise (without focusing very hard on it) that it has actually blurred anything.
The code is (quite long, sorry):
Bitmap screenshot = null;
void BlurForm()
{
/*
The best time (I assume) is to change the blur when the user as left the window,
as opposed to doing it when they want to open the window, since this may be a
little intensive.
*/
screenshot = null;
blurred.Image = null; // This variable is the PictureBox that's on the Form
screenshot = Screenshot.TakeSnapshot(this);
BitmapFilter.GaussianBlur(screenshot, 4);
blurred.Image = screenshot;
blurred.SendToBack();
}
public class ConvMatrix
{
public int TopLeft = 0, TopMid = 0, TopRight = 0;
public int MidLeft = 0, Pixel = 1, MidRight = 0;
public int BottomLeft = 0, BottomMid = 0, BottomRight = 0;
public int Factor = 1;
public int Offset = 0;
public void SetAll(int nVal)
{
TopLeft = TopMid = TopRight = MidLeft = Pixel = MidRight = BottomLeft = BottomMid = BottomRight = nVal;
}
}
public class BitmapFilter
{
private static bool Conv3x3(Bitmap b, ConvMatrix m)
{
// Avoid divide by zero errors
if (0 == m.Factor) return false;
Bitmap bSrc = (Bitmap)b.Clone();
// GDI+ still lies to us - the return format is BGR, NOT RGB.
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
int stride2 = stride * 2;
System.IntPtr Scan0 = bmData.Scan0;
System.IntPtr SrcScan0 = bmSrc.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
byte* pSrc = (byte*)(void*)SrcScan0;
int nOffset = stride + 6 - b.Width * 3;
int nWidth = b.Width - 2;
int nHeight = b.Height - 2;
int nPixel;
for (int y = 0; y < nHeight; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
nPixel = ((((pSrc[2] * m.TopLeft) + (pSrc[5] * m.TopMid) + (pSrc[8] * m.TopRight) +
(pSrc[2 + stride] * m.MidLeft) + (pSrc[5 + stride] * m.Pixel) + (pSrc[8 + stride] * m.MidRight) +
(pSrc[2 + stride2] * m.BottomLeft) + (pSrc[5 + stride2] * m.BottomMid) + (pSrc[8 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[5 + stride] = (byte)nPixel;
nPixel = ((((pSrc[1] * m.TopLeft) + (pSrc[4] * m.TopMid) + (pSrc[7] * m.TopRight) +
(pSrc[1 + stride] * m.MidLeft) + (pSrc[4 + stride] * m.Pixel) + (pSrc[7 + stride] * m.MidRight) +
(pSrc[1 + stride2] * m.BottomLeft) + (pSrc[4 + stride2] * m.BottomMid) + (pSrc[7 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[4 + stride] = (byte)nPixel;
nPixel = ((((pSrc[0] * m.TopLeft) + (pSrc[3] * m.TopMid) + (pSrc[6] * m.TopRight) +
(pSrc[0 + stride] * m.MidLeft) + (pSrc[3 + stride] * m.Pixel) + (pSrc[6 + stride] * m.MidRight) +
(pSrc[0 + stride2] * m.BottomLeft) + (pSrc[3 + stride2] * m.BottomMid) + (pSrc[6 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[3 + stride] = (byte)nPixel;
p += 3;
pSrc += 3;
}
p += nOffset;
pSrc += nOffset;
}
}
b.UnlockBits(bmData);
bSrc.UnlockBits(bmSrc);
return true;
}
public static bool GaussianBlur(Bitmap b, int nWeight /* default to 4*/)
{
ConvMatrix m = new ConvMatrix();
m.SetAll(1);
m.Pixel = nWeight;
m.TopMid = m.MidLeft = m.MidRight = m.BottomMid = 2;
m.Factor = nWeight + 12;
return BitmapFilter.Conv3x3(b, m);
}
}
class Screenshot
{
public static Bitmap TakeSnapshot(Control ctl)
{
Bitmap bmp = new Bitmap(ctl.Size.Width, ctl.Size.Height);
using (Graphics g = System.Drawing.Graphics.FromImage(bmp))
{
g.CopyFromScreen(
ctl.PointToScreen(ctl.ClientRectangle.Location),
new Point(0, 0), ctl.ClientRectangle.Size
);
}
return bmp;
}
}
I've tried changing every single value in there and it doesn't matter which value(s) I change, I just can't get it to have a stronger blur effect. How can I increase the strength of the Gaussian Blur?
The Form is 80% Opaque, so the blur effect you would see is the windows wallpaper or whatever is behind the form - but blurred. Only problem (as I've said) is that the blur is not strong at all.
After just glancing at the code, it looks to me like the blur filter is hardcoded to only operate in a 3x3 area at a time, severely restricting how blurred it can make an image.
Unless I'm mistaken, the only way to significantly increase the amount of blurring is to find a more sophisticated implementation.
I have an image in an array and I'm saving it this way:
ImageConverter ic = new ImageConverter();
Image img = (Image)ic.ConvertFrom(Jpeg);
Bitmap bitmap1 = new Bitmap(img);
string saveString = "c:\\M_files\\new_pics\\" + pictureCounter + ".jpg";
bitmap1.Save(saveString, System.Drawing.Imaging.ImageFormat.Jpeg);
It works but I need it to be faster since its an image from camera that needs to stream. Is there a faster way? My array is in bytes then I use the container to convert to bitmap then save as jpeg.
try this code, this works fast and efficient, in this piece of code you can convert a picture to JPEG specitfying the width and height, and can pass as many number of images as you want. You can modify it to your own Requirement.
public void CreateThumbnail(string[] b, double wid, double hght, bool Isprint)
{
string[] path;
path = new string [64];
path = b;
string saveath = "i:\\check\\a test\\";
for (int i = 0; i < b.Length; i++)
{
DirectoryInfo dir = new DirectoryInfo(path[i]);
string dir1 = dir.ToString();
dir1 = dir1.Substring(dir1.LastIndexOf("\\"));
FileInfo[] files1 = dir.GetFiles();
foreach (FileInfo f in files1)
{
string gh = f.ToString();
try
{
System.Drawing.Image myThumbnail150;
System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
System.Drawing.Image imagesize = System.Drawing.Image.FromFile(f.FullName);
Bitmap bitmapNew = new Bitmap(imagesize);
double maxWidth = wid;
double maxHeight = hght;
int w = imagesize.Width;
int h = imagesize.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
myThumbnail150 = bitmapNew.GetThumbnailImage((int)newWidth, (int)newHeight, myCallback, IntPtr.Zero);
string ext = Path.GetExtension(f.Name);
if (!Directory.Exists(saveath + dir1))
{
Directory.CreateDirectory(saveath + dir1);
myThumbnail150.Save(saveath + dir1 + "\\" + f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
else if(Directory.Exists(saveath+dir1))
{
myThumbnail150.Save(saveath + dir1+" \\"+ f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (Exception ex)
{
Console.WriteLine("something went wrong" + ex.ToString());
}
}
}
}
struct BitmapDataAccessor
{
private readonly byte[] data;
private readonly int[] rowStarts;
public readonly int Height;
public readonly int Width;
public BitmapDataAccessor(byte[] data, int width, int height)
{
this.data = data;
this.Height = height;
this.Width = width;
rowStarts = new int[height];
for (int y = 0; y < Height; y++)
rowStarts[y] = y * width;
}
public byte this[int x, int y, int color] // Maybe use an enum with Red = 0, Green = 1, and Blue = 2 members?
{
get { return data[(rowStarts[y] + x) * 3 + color]; }
set { data[(rowStarts[y] + x) * 3 + color] = value; }
}
public byte[] Data
{
get { return data; }
}
}
public static byte[, ,] Bitmap2Byte(Bitmap obraz)
{
int h = obraz.Height;
int w = obraz.Width;
byte[, ,] wynik = new byte[w, h, 3];
BitmapData bd = obraz.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int bytes = Math.Abs(bd.Stride) * h;
byte[] rgbValues = new byte[bytes];
IntPtr ptr = bd.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
BitmapDataAccessor bda = new BitmapDataAccessor(rgbValues, w, h);
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
wynik[j, i, 0] = bda[j, i, 2];
wynik[j, i, 1] = bda[j, i, 1];
wynik[j, i, 2] = bda[j, i, 0];
}
}
obraz.UnlockBits(bd);
return wynik;
}
public static Bitmap Byte2Bitmap(byte[, ,] tablica)
{
if (tablica.GetLength(2) != 3)
{
throw new NieprawidlowyWymiarTablicyException();
}
int w = tablica.GetLength(0);
int h = tablica.GetLength(1);
Bitmap obraz = new Bitmap(w, h, PixelFormat.Format24bppRgb);
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
Color kol = Color.FromArgb(tablica[i, j, 0], tablica[i, j, 1], tablica[i, j, 2]);
obraz.SetPixel(i, j, kol);
}
}
return obraz;
}
Now, if I do:
private void btnLoad_Click(object sender, EventArgs e)
{
if (dgOpenFile.ShowDialog() == DialogResult.OK)
{
try
{
Bitmap img = new Bitmap(dgOpenFile.FileName);
byte[, ,] tab = Grafika.Bitmap2Byte(img);
picture.Image = Grafika.Byte2Bitmap(tab);
picture.Size = img.Size;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Most of pictures are handled correctly butsome not.
Example of picture that doesn't work:
(source: ifotos.pl)
It produce following result (this is only fragment of picture) :
(source: ifotos.pl)
Why is that?
You need to account for BitmapData.Stride when you access the data.
EDIT:
Here is a solution that I use to copy a DirectX surface to a Bitmap. The idea is the same, but you'll need to modify it slightly. I copy one scanline of the image at a time with a call to RtlMoveMemory (P/Invoke to kernel32.dll)
//// Snippet
int pitch;
int bytesPerPixel = 4;
Rectangle lockRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
// Lock the bitmap
GraphicsStream surfacedata = surface.LockRectangle(LockFlags.ReadOnly, out pitch);
BitmapData bitmapdata = bitmap.LockBits(lockRectangle, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
// Copy surface to bitmap
for (int scanline = 0; scanline < bitmap.Height; ++scanline)
{
byte* dest = (byte*)bitmapdata.Scan0 + (scanline * bitmap.Width * bytesPerPixel);
byte* source = (byte*)surfacedata.InternalData + (scanline * pitch);
RtlMoveMemory(new IntPtr(dest), new IntPtr(source), (bitmap.Width * bytesPerPixel));
}
////
EDIT #2:
Check this out: Stride/Pitch Tutorial
It is all aimed at DirectX but the concept is the same.
It seems the memory allocated for bitmaps must be aligned on a 32-bit boundary and so there is possibly padding on some of the images due to their size. As you have a 24-bit pixel here then some line widths will end on a 32-bit others will not. You need to use the following formula to work out the padding being used and then account for it:
int padding = bd.Stride - (((w * 24) + 7) / 8);
You might want to load your byte array using GetPixel(x,y) rather than going through the whole transform to byte array before you start reading pixels.
Thanx to #Lazarus and tbridge I managed how to do this.
First we need to calculate padding in Bitmap2Byte:
int padding = bd.Stride - (((w * 24) + 7) / 8);
and pass it to BitmapDataAccessor and modify the line
this.Width = width;
to
this.Width = width + (4-padding)%4;
That's all. Thanx guys.