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.
Related
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?
I'm currently trying to learn how to use multithreading in C# and I'm really struggling with letting the user choose how many threads to use from a UI.
I have, with the help of a few sources, written the code to mask a image using the median filter and now want to apply multithreading (1-64).
I know how threads work, but I'm struggling to find a way to implement it in this program.
Can someone please help me understand by not showing me the code needed, but explaining where threading should be implemented and how to allow the user to choose the amount of threads.
Code
class ExtBitmap
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing.Imaging;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
namespace EilandFotoPRAK1
{
public static class ExtBitmap
{
public static Bitmap CopyToSquareCanvas(this Bitmap sourceBitmap, int canvasWidthLenght)
{
float ratio = 1.0f;
int maxSide = sourceBitmap.Width > sourceBitmap.Height ?
sourceBitmap.Width : sourceBitmap.Height;
ratio = (float)maxSide / (float)canvasWidthLenght;
Bitmap bitmapResult = (sourceBitmap.Width > sourceBitmap.Height ?
new Bitmap(canvasWidthLenght, (int)(sourceBitmap.Height / ratio))
: new Bitmap((int)(sourceBitmap.Width / ratio), canvasWidthLenght));
using (Graphics graphicsResult = Graphics.FromImage(bitmapResult))
{
graphicsResult.CompositingQuality = CompositingQuality.HighQuality;
graphicsResult.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsResult.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphicsResult.DrawImage(sourceBitmap,
new Rectangle(0, 0,
bitmapResult.Width, bitmapResult.Height),
new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
GraphicsUnit.Pixel);
graphicsResult.Flush();
}
return bitmapResult;
}
public static Bitmap MedianFilter(this Bitmap sourceBitmap,
int matrixSize,
int bias = 0,
bool grayscale = false)
{
BitmapData sourceData =
sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride *
sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride *
sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0,
pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
if (grayscale == true)
{
float rgb = 0;
for (int k = 0; k < pixelBuffer.Length; k += 4)
{
rgb = pixelBuffer[k] * 0.11f;
rgb += pixelBuffer[k + 1] * 0.59f;
rgb += pixelBuffer[k + 2] * 0.3f;
pixelBuffer[k] = (byte)rgb;
pixelBuffer[k + 1] = pixelBuffer[k];
pixelBuffer[k + 2] = pixelBuffer[k];
pixelBuffer[k + 3] = 255;
}
}
int filterOffset = (matrixSize - 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
List<int> neighbourPixels = new List<int>();
byte[] middlePixel;
for (int offsetY = filterOffset; offsetY <
sourceBitmap.Height - filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX <
sourceBitmap.Width - filterOffset; offsetX++)
{
byteOffset = offsetY *
sourceData.Stride +
offsetX * 4;
neighbourPixels.Clear();
for (int filterY = -filterOffset;
filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset;
filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset +
(filterX * 4) +
(filterY * sourceData.Stride);
neighbourPixels.Add(BitConverter.ToInt32(
pixelBuffer, calcOffset));
}
}
neighbourPixels.Sort();
middlePixel = BitConverter.GetBytes(
neighbourPixels[filterOffset]);
resultBuffer[byteOffset] = middlePixel[0];
resultBuffer[byteOffset + 1] = middlePixel[1];
resultBuffer[byteOffset + 2] = middlePixel[2];
resultBuffer[byteOffset + 3] = middlePixel[3];
}
}
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width,
sourceBitmap.Height);
BitmapData resultData =
resultBitmap.LockBits(new Rectangle(0, 0,
resultBitmap.Width, resultBitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0,
resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap;
}
}
}
FrmPhoto class
---------------------------------------How the GUI Looks
public partial class FrmPhoto : Form
{
private Bitmap originalBitmap = null;
private Bitmap previewBitmap = null;
private Bitmap resultBitmap = null;
public FrmPhoto()
{
InitializeComponent();
cmbEdge.SelectedIndex = 0;
}
private void btnOpen_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Select an image file.";
dialog.Filter = "Png Images(*.png)|*.png|Jpeg Images(*.jpg)|*.jpg";
dialog.Filter += "|Bitmap Images(*.bmp)|*.bmp";
if (dialog.ShowDialog() == DialogResult.OK)
{
StreamReader streamReader = new StreamReader(dialog.FileName);
originalBitmap = (Bitmap)Bitmap.FromStream(streamReader.BaseStream);
streamReader.Close();
previewBitmap = originalBitmap.CopyToSquareCanvas(pix1.Width);
pix1.Image = previewBitmap;
}
}
private void ApplyFilter(bool preview)
{
if (previewBitmap == null || cmbEdge.SelectedIndex == -1)
{
return;
}
Bitmap selectedSource = null;
Bitmap bitmapResult = null;
if (preview == true)
{
selectedSource = previewBitmap;
}
else
{
selectedSource = originalBitmap;
}
if (selectedSource != null)
{
if (cmbEdge.SelectedItem.ToString() == "None")
{
bitmapResult = selectedSource;
}
else if (cmbEdge.SelectedItem.ToString() == "Median 3x3")
{
bitmapResult = selectedSource.MedianFilter(3);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 5x5")
{
bitmapResult = selectedSource.MedianFilter(5);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 7x7")
{
bitmapResult = selectedSource.MedianFilter(7);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 9x9")
{
bitmapResult = selectedSource.MedianFilter(9);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 11x11")
{
bitmapResult = selectedSource.MedianFilter(11);
}
else if (cmbEdge.SelectedItem.ToString() == "Median 13x13")
{
bitmapResult = selectedSource.MedianFilter(13);
}
}
if (bitmapResult != null)
{
if (preview == true)
{
pix1.Image = bitmapResult;
}
else
{
resultBitmap = bitmapResult;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
ApplyFilter(true);
}
}
We can use Parallel.For method that will make parallelization for us.
We have to make all variables that are modified inside the parallel loop are local.
int filterOffset = (matrixSize - 1) / 2;
//int calcOffset = 0;
//int byteOffset = 0;
//List<int> neighbourPixels = new List<int>();
//byte[] middlePixel;
var w = sourceBitmap.Width - filterOffset;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
{
Parallel.For(filterOffset, w, offsetX =>
{
var byteOffset = offsetY * sourceData.Stride + offsetX * 4; // local
var neighbourPixels = new List<int>(); // local
//neighbourPixels.Clear();
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
var calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride); // local
neighbourPixels.Add(BitConverter.ToInt32(pixelBuffer, calcOffset));
}
}
neighbourPixels.Sort();
var middlePixel = BitConverter.GetBytes(neighbourPixels[filterOffset]); // local
resultBuffer[byteOffset] = middlePixel[0];
resultBuffer[byteOffset + 1] = middlePixel[1];
resultBuffer[byteOffset + 2] = middlePixel[2];
resultBuffer[byteOffset + 3] = middlePixel[3];
});
};
This gives more than a two time increase in performance on my machine (2 physical cores, 4 logical).
Parallelization of the outer loop would certainly give more acceleration. However, to make it much more difficult.
In a similar way it is possible to make a parallelization of the grayscale filter.
I am using the following Kernel,
double[,] kernel = new double[,] { { -1, -1, -1, },
{ -1, 9, -1, },
{ -1, -1, -1, }, };
The following code seems to be blurring the input image, rather than Sharpening.
What could have been the issue here?
Here is the entire VS2013 solution.
The original image,
The resulting blurred image,
I have written the following code to Sharpen an image,
public static Bitmap FftSharpen(Bitmap image, double [,] mask)
{
if (image.PixelFormat == PixelFormat.Format8bppIndexed)
{
Bitmap imageClone = (Bitmap)image.Clone();
double[,] maskClone = (double[,])mask.Clone();
Complex[,] cPaddedImage = ImageDataConverter.ToComplex(imageClone);
Complex[,] cPaddedMask = ImageDataConverter.ToComplex(maskClone);
Complex[,] cConvolved = Convolution.Convolve(cPaddedImage, cPaddedMask);
return ImageDataConverter.ToBitmap(cConvolved);
}
else
{
throw new Exception("not a grascale");
}
}
.
.
P.S.
The following is my convolution code,
public static class Convolution
{
public static Complex[,] Convolve(Complex[,] image, Complex[,] mask)
{
Complex[,] convolve = null;
int imageWidth = image.GetLength(0);
int imageHeight = image.GetLength(1);
int maskWidth = mask.GetLength(0);
int maskeHeight = mask.GetLength(1);
if (imageWidth == maskWidth && imageHeight == maskeHeight)
{
FourierTransform ftForImage = new FourierTransform(image); ftForImage.ForwardFFT();
FourierTransform ftForMask = new FourierTransform(mask); ftForMask.ForwardFFT();
Complex[,] fftImage = ftForImage.FourierImageComplex;
Complex[,] fftKernel = ftForMask.FourierImageComplex;
Complex[,] fftConvolved = new Complex[imageWidth, imageHeight];
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
fftConvolved[i, j] = fftImage[i, j] * fftKernel[i, j];
}
}
FourierTransform ftForConv = new FourierTransform();
ftForConv.InverseFFT(fftConvolved);
convolve = ftForConv.GrayscaleImageComplex;
Rescale(convolve);
convolve = FourierShifter.FFTShift(convolve);
}
else
{
throw new Exception("padding needed");
}
return convolve;
}
//Rescale values between 0 and 255.
private static void Rescale(Complex[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double maxAmp = 0.0;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
maxAmp = Math.Max(maxAmp, convolve[i, j].Magnitude);
}
}
double scale = 255.0 / maxAmp;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
convolve[i, j] = new Complex(convolve[i, j].Real * scale, convolve[i, j].Imaginary * scale);
maxAmp = Math.Max(maxAmp, convolve[i, j].Magnitude);
}
}
}
}
Similarly to your other question, the kernel is obtained from an unsigned Bitmap which result in the effective kernel
255 255 255
255 9 255
255 255 255
instead of the expected
-1 -1 -1
-1 9 -1
-1 -1 -1
A solution would again be to convert the bitmap to signed values. Alternatively, since the code provided in this question also supports the numerical kernel to be provided directly to FftSharpen, you could pad _numericalKernel with:
public class MatrixPadder
{
public static double[,] Pad(double[,] image, int newWidth, int newHeight)
{
int width = image.GetLength(0);
int height = image.GetLength(1);
/*
It is always guaranteed that,
width < newWidth
and
height < newHeight
*/
if ((width < newWidth && height < newHeight)
|| (width < newWidth && height == newHeight)
|| (width == newWidth && height < newHeight))
{
double[,] paddedImage = new double[newWidth, newHeight];
int startPointX = (int)Math.Ceiling((double)(newWidth - width) / (double)2) - 1;
int startPointY = (int)Math.Ceiling((double)(newHeight - height) / (double)2) - 1;
for (int y = startPointY; y < (startPointY + height); y++)
{
for (int x = startPointX; x < (startPointX + width); x++)
{
int xxx = x - startPointX;
int yyy = y - startPointY;
paddedImage[x, y] = image[xxx, yyy];
}
}
return paddedImage;
}
else if (width == newWidth && height == newHeight)
{
return image;
}
else
{
throw new Exception("Pad() -- threw an exception");
}
}
}
which you could call from filterButton_Click using:
if (_convolutionType == ConvolutionType.FFT)
{
double[,] paddedmask = MatrixPadder.Pad(_numericalKernel,
_paddedImage.Width,
_paddedImage.Height);
sharpened = SharpenFilter.FftSharpen(_paddedImage, paddedmask);
}
Also adjusting the Rescale function as shown in my other answer should then give you the desired sharpened image:
using (System.IO.FileStream fs = File.Open(GetCurrentWallpaper(), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
I'm writing an app that needs to open the current wallpaper like this every time it's changed.
I first access the registry to get the wallpaper's path (GetCurrentWallpaper), and use a FileSystemWatcher to do stuff with the wallpaper when it's changed.
Oddly, it only works once. If the wallpaper is accessed a second time(it doesn't matter how long I wait), my app crashed with an IOException telling me that I can't access the file because it's already in use.
If I restart the app, it can access the file again, but, as mentioned above, only once. Else it crashes.
Is there anything I can do in order to gain access to that file?
Edit: More code:
using (System.IO.FileStream fs = File.Open(GetCurrentWallpaper(), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (Bitmap orig = (Bitmap)Bitmap.FromStream(fs, true, false)) {
int width = Convert.ToInt32(orig.Width / 3);
int height = Convert.ToInt32(orig.Height / 3);
Rectangle rect = new Rectangle(0, 0, width, height);
using (Bitmap bmp = new Bitmap(width, height)) {
using (Graphics bmpg = Graphics.FromImage(bmp)) {
col = ColorHelper.CalculateAverageColor(bmp, true, 20);
fs.Flush();
fs.Close();
}
}
}
}
//this is in the ColorHelper class
public static System.Drawing.Color CalculateAverageColor(Bitmap bm, bool dropPixels, int colorDiff) {
int width = bm.Width;
int height = bm.Height;
int red = 0;
int green = 0;
int blue = 0;
int minDiversion = colorDiff;
int dropped = 0;
long[] totals = new long[] { 0, 0, 0 };
int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4;
BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;
unsafe {
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int idx = (y * stride) + x * bppModifier;
red = p[idx + 2];
green = p[idx + 1];
blue = p[idx];
if (dropPixels) {
if (Math.Abs(red - green) > minDiversion || Math.Abs(red - blue) > minDiversion || Math.Abs(green - blue) > minDiversion) {
totals[2] += red;
totals[1] += green;
totals[0] += blue;
} else {
dropped++;
}
} else {
totals[2] += red;
totals[1] += green;
totals[0] += blue;
}
}
}
}
int count = width * height - dropped;
int avgR;
int avgG;
int avgB;
if (count > 0) {
avgR = (int)(totals[2] / count);
avgG = (int)(totals[1] / count);
avgB = (int)(totals[0] / count);
} else {
avgR = (int)(totals[2]);
avgG = (int)(totals[1]);
avgB = (int)(totals[0]);
}
bm.UnlockBits(srcData);
return System.Drawing.Color.FromArgb(avgR, avgG, avgB);
}
If you use fs.Close() than you will release the file for further read operations.
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.