when I create a bitmap like this:
var testImage = new Bitmap(320, 240);
var testDataLock = testImage.LockBits(new Rectangle(new Point(), testImage.Size),
System.Drawing.Imaging.ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
var aaa = CamData.ToArray();
UInt32 lOffset = 0;
UInt32 lPos = 0;
byte* lDst = (byte*)testDataLock.Scan0;
byte bitshift = 8;
fixed (UInt16* lSrc = aaa)
{
while (lOffset < testImage.Width * testImage.Height)
{
lDst[lPos] = (byte)(lSrc[lOffset] >> bitshift);
lDst[lPos + 1] = lDst[lPos];
lDst[lPos + 2] = lDst[lPos];
lOffset++;
lPos += 3;
// take care of the padding in the destination bitmap
if ((lOffset % testImage.Width) == 0)
lPos += (UInt32)testDataLock.Stride - (uint)(testImage.Width * 3);
}
}
}
testImage.UnlockBits(testDataLock);
testImage.Save(#"H:\Test.bmp");
I alway get an error while trying to open this file with an visualisation lib:
Unknown file type! H:\test.bmp is not a Windows BMP file!
but in windows I can open the file with the viewer etc... there are no problems
does anybody know why I get this error?
thanks
you can save a System.Drawing.Bitmap to a valid windows .bmp like this:
//bmp is a System.Drawing.Bitmap
bmp.Save("MyBitmap.bmp", ImageFormat.Bmp);
The second parameter (which you did not include) specifies the format in which the bitmap must be saved.
Also, be sure to check if your visualisation lib supports 24Bit Per Pixel bitmaps,
since this is the format you are creating your bitmap in.
see:
PixelFormat.Format24bppRgb
As you can read at MSDN in the Remarks section your image will be saved as PNG if no encoder is specified.
Related
I want to read a dicom image using simpleitk, convert it into a bitmap and then display the result in a pictureBox. But when I'm trying to do this, an ArgumentException is thrown. How can I solve this?
Here is my code:
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Open";
dialog.Filter = "DICOM Files (*.dcm;*.dic)|*.dcm;*.dic|All Files (*.*)|*.*";
dialog.ShowDialog();
if (dialog.FileName != "")
{
using (sitk.ImageFileReader reader = new sitk.ImageFileReader())
{
reader.SetFileName(dialog.FileName);
reader.SetOutputPixelType(sitk.PixelIDValueEnum.sitkFloat32);
sitk.Image image = reader.Execute();
var castedImage = sitk.SimpleITK.Cast(image,
sitk.PixelIDValueEnum.sitkFloat32);
var size = castedImage.GetSize();
int length = size.Aggregate(1, (current, i) => current * (int)i);
IntPtr buffer = castedImage.GetBufferAsFloat();
// Declare an array to hold the bytes of the bitmap.
byte[] rgbValues = new byte[length];
// Copy the RGB values into the array.
Marshal.Copy(buffer, rgbValues, 0, length);
Stream stream = new MemoryStream(rgbValues);
Bitmap newBitmap = new Bitmap(stream);
//I have tried in this way, but it generated ArgumentException too
//Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetDepth(), PixelFormat.Format8bppIndexed, buffer);
Obraz.pic.Image = newBitmap;
}
}
Thank you for your comments and attempts to help. After consultations and my own searching on the internet I solved this issue. The first problem was the inadequate representation of pixel image. I had to change Float32 to UInt8 to provide an eight-bit for pixel.
var castedImage = sitk.SimpleITK.Cast(image2, sitk.PixelIDValueEnum.sitkUInt8);
Then I would already create a Bitmap using the constructor that was was commented out in question, but with (int)image.GetWidth() instead of (int)image.GetDepth().
Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetWidth(), PixelFormat.Format8bppIndexed, buffer);
Unfortunately, a new problem appeared. The image, that was supposed to be in gray scale, was displayed in strange colors. But I found the solution here
ColorPalette pal = newBitmap.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
newBitmap.Palette = pal; // you need to re-set this property to force the new ColorPalette
I'm developing a small C# tool that must be able to load a TIFF image, crop the image to a certain size, and save it as a PNG file.
I have large greyscale TIFF images of about 28000x256 pixels with 32-bit bit depth. When I try to process the images with my tool, it just outputs a blank white image.
Also, when I try to open the original TIFF images (not the ones processed with my tool) with the Windows Photo Viewer, it also shows a blank white image. Some other applications, e.g. ImageJ, display the image correctly. What is the problem here?
My code to load the images looks as follows:
Image image = Bitmap.FromFile(path.LocalPath);
int width = image.Width;
int height = image.Height;
Bitmap bmp = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bmp);
The problem is that C# (or better said the underlying API) can't handle Greyscale images with a Colordepth greater than 8bit.
I'd suggest using LibTiff.NET for Handling TIFF images.
When i faced such an problem, i loaded the TIFF image raw Data into an array
using (var inputImage = Tiff.Open(image, "r"))
{
width = inputImage.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
height = inputImage.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
inputImageData = new byte[width * height * bytePerPixel];
var offset = 0;
for (int i = 0; i < inputImage.NumberOfStrips(); i++)
{
offset += inputImage.ReadRawStrip(i, inputImageData, offset, (int)inputImage.RawStripSize(i));
}
}
The bytes then have to be converted into an array of uint (in my case, imagedata was only 16 bit, so i used ushort) Remember to take care of Endianness of the data!
// has to be done by hand to ensure endiannes is kept correctly.
var outputImageData = new ushort[inputImageData.Length / 2];
for (var i = 0; i < outputImageData.Length; i++)
{
outputImageData[i] = (ushort)((inputImageData[i * 2 + 1]) + (ushort)(inputImageData[i * 2] << 8));
}
You can then manipulate the image using normal Array Operations. I'd suggest you to use normal Array operations and not Lambda-Expressions, as they are much faster. (in My Scenario 100s vs 2s Runtime)
Finally you can save the image using LibTiff again
using (var output = Tiff.Open(imageout, "w"))
{
output.SetField(TiffTag.IMAGEWIDTH, width);
output.SetField(TiffTag.IMAGELENGTH, height);
output.SetField(TiffTag.SAMPLESPERPIXEL, 1);
output.SetField(TiffTag.BITSPERSAMPLE, 16);
output.SetField(TiffTag.ROWSPERSTRIP, height);
output.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISBLACK);
output.SetField(TiffTag.FILLORDER, FillOrder.MSB2LSB);
// Transform to Byte-Array
var buffer = new byte[outputImageData.Length * sizeof(ushort)];
Buffer.BlockCopy(outputImageData, 0, buffer, 0, buffer.Length);
// Write it to Image
output.WriteRawStrip(0, buffer, buffer.Length);
}
So here is my problem
I've used a scanner to scan an object in greyscale and convert it into a JPEG format to be analyzed by a C# program. The image's pixelformat is 8BppIndexed.
When I import this image into C# and draw a histogram of it, I only see 16 grayscale values, like this:
All the values in between these peaks are 0.
This is what the normal histogram should look like (don't mind the colors, this histogram is made with another tool):
The first histogram (int[]) is formed with this code:
public static int[] GetHistogram(Bitmap b)
{
int[] myHistogram = new int[256];
for (int i = 0; i < myHistogram.Length; i++)
myHistogram[i] = 0;
BitmapData bmData = null;
try
{
//Lock it fixed with 32bpp
bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int scanline = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nWidth = b.Width;
int nHeight = b.Height;
for (int y = 0; y < nHeight; y++)
{
for (int x = 0; x < nWidth; x++)
{
long Temp = 0;
Temp += p[0]; // p[0] - blue, p[1] - green , p[2]-red
Temp += p[1];
Temp += p[2];
Temp = (int)Temp / 3;
myHistogram[Temp]++;
//we do not need to use any offset, we always can increment by pixelsize when
//locking in 32bppArgb - mode
p += 4;
}
}
}
b.UnlockBits(bmData);
}
catch
{
try
{
b.UnlockBits(bmData);
}
catch
{
}
}
return myHistogram;
}
To be sure this code is not the problem, I've tried using the AForge.Math.Histogram way and even a for - in - for loop to iterate through all pixels. Each time I get the same result.
Now here is the funny part(s):
When I draw the histogram with any other tool (used 3 others), I get
a normal histogram. This tells me that the information is within the image, but my code just can't get it out.
When I scan the exact same object and set the settings to export the image into a .bmp file, c# is able to draw a normal histogram
With another random .jpg image I found on my computer, c# is able to draw a normal
histogram.
These points tell me that there is probably something wrong with the way that I import the image into my code, so I tried different ways to import the image:
Bitmap bmp = (Bitmap)Bitmap.FromFile(path);
or
Bitmap bmp = AForge.Imaging.Image.FromFile(path);
or
Stream imageStreamSource = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
System.Windows.Media.Imaging.JpegBitmapDecoder decoder = new System.Windows.Media.Imaging.JpegBitmapDecoder(imageStreamSource, System.Windows.Media.Imaging.BitmapCreateOptions.PreservePixelFormat, System.Windows.Media.Imaging.BitmapCacheOption.Default);
System.Windows.Media.Imaging.BitmapSource bitmapSource = decoder.Frames[0];
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = bitmapSource;
image.Stretch = System.Windows.Media.Stretch.None;
MemoryStream ms = new MemoryStream();
var encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(image.Source as System.Windows.Media.Imaging.BitmapSource));
encoder.Save(ms);
ms.Flush();
System.Drawing.Image myImage = System.Drawing.Image.FromStream(ms);
Bitmap bmp = (Bitmap)Bitmap.FromStream(ms);
None of which gave a different histogram than the one with just 16 results.
I can not use the .bmp extension in my scanner, because I need to make a great many images and one .bmp image is around 200mb (yea, the images need a high resolution), while the .jpg is only around 30mb. Plus I've already made many .jpg images that can not be remade because the objects that have been scanned no longer exist.
NOTE: I know that using the .jpg extension is a lossy way to compress the images. That is not the current issue.
This is what a histogram, created with the exact same code as the first one, looks like with another random .jpg image from my computer:
Does this sound familiar to anyone? I feel like I've tried everything. Is there another way to solve this problem that I have not yet found?
EDIT
I thought I had found an extremely dirty way to fix my problem, but it does change the histogram:
Bitmap temp = (Bitmap)Bitmap.FromFile(m_sourceImageFileName);
if (temp.PixelFormat == PixelFormat.Format8bppIndexed ||
temp.PixelFormat == PixelFormat.Format4bppIndexed ||
temp.PixelFormat == PixelFormat.Format1bppIndexed ||
temp.PixelFormat == PixelFormat.Indexed)
{
//Change pixelformat to a format that AForge can work with
Bitmap tmp = temp.Clone(new Rectangle(0, 0, temp.Width, temp.Height), PixelFormat.Format24bppRgb);
//This is a super dirty way to make sure the histogram shows more than 16 grey values.
for (int i = 0; true; i++)
{
if (!File.Exists(m_sourceImageFileName + i + ".jpg"))
{
tmp.Save(m_sourceImageFileName + i + ".jpg");
tmp.Dispose();
temp = AForge.Imaging.Image.FromFile(m_sourceImageFileName + i + ".jpg");
File.Delete(m_sourceImageFileName + i + ".jpg");
break;
}
}
}
Bitmap properImage = temp;
This is the new histogram:
As you can see, it's not the same as what the histogram should look like.
I found out that the problem might be because the image is an 8bppIndexed jpeg image, and jpeg only supports 24bppRgb images. Any solutions?
I think the clue is in the type being "indexed" in your second line. There are probably only 16 colours in the lookup table. Can you post your original scanned image so we can see if there are really more shades in it? If not, try using ImageMagick to count the colours
Like this to get a histogram:
convert yourimage.jpg -format %c histogram:info:-
convert yourimage.jpg -colorspace rgb -colors 256 -depth 8 -format "%c" histogram:info:
Or count the unique colours like this:
identify -verbose yourimage.jpg | grep -i colors:
Or dump all the pixels like this:
convert yourimage.jpg -colorspace rgb -colors 256 -depth 8 txt:
Well, I solved it by opening the JPEG and saving it as bmp with the ImageJ library in java. I've made a .jar file from the code and I use this code to get the bmp into my c# code:
string extension = m_sourceImageFileName.Substring(m_sourceImageFileName.LastIndexOf("."), m_sourceImageFileName.Length - m_sourceImageFileName.LastIndexOf("."));
int exitcode;
ProcessStartInfo ProcessInfo;
Process process;
ProcessInfo = new ProcessStartInfo("java.exe", #"-jar ""C:\Users\stevenh\Documents\Visual Studio 2010\Projects\BlackSpotDetection V2.0\ConvertToBmp\dist\ConvertToBmp.jar"" " + extension + " " + m_sourceImageFileName + " " + m_addedImageName);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
// redirecting standard output and error
ProcessInfo.RedirectStandardError = true;
ProcessInfo.RedirectStandardOutput = true;
process = Process.Start(ProcessInfo);
process.WaitForExit();
//Reading output and error
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitcode = process.ExitCode;
if (exitcode != 0)
{
statusLabel.Text = output;
MessageBox.Show("Error in external process: converting image to bmp.\n" + error);
//Exit code '0' denotes success and '1' denotes failure
return;
}
else
statusLabel.Text = "Awesomeness";
process.Close();
Bitmap realImage = AForge.Imaging.Image.FromFile(m_addedImageName);
File.Delete(m_addedImageName);
The jar will receive the extension, m_sourceImageFileName and m_addedImageFileName. It will open the sourceImage and save it under the name of m_addedImageFileName
I'm using the AForge library to open the image, because this library doesn't lock the image while it's opened, which makes me able to delete the 'home-made' image.
I have a page that sends html5 canvas data, encoded as a base64 bmp image (using this algorithm http://devpro.it/code/216.html) to a serverside process that converts it into a System.Drawing.Image object and does some operations on it.
In my local environment, this works just fine, but on my ec2 instance I get the following error:
System.ArgumentException: Parameter is not valid. at
System.Drawing.Image.FromStream(Stream stream, Boolean
useEmbeddedColorManagement, Boolean validateImageData) at
System.Drawing.Image.FromStream(Stream stream, Boolean
useEmbeddedColorManagement)
My code looks as follows:
System.Drawing.Image image = null;
string b64string = "...";
byte[] sf = Convert.FromBase64String(b64string );
using (MemoryStream s = new MemoryStream(sf, 0, sf.Length))
{
image = System.Drawing.Image.FromStream(s, false);
}
...
Here's a text file with a sample b64string that I'm using to test: https://docs.google.com/leaf?id=0BzVLGmig1YZ3MTM0ODBiNjItNzk4Yi00MzI5LWI5ZWMtMzU1OThlNWEyMTU5&hl=en_US
I've also tried the following and had the same results:
System.Drawing.ImageConverter converter = new System.Drawing.ImageConverter();
image = converter.ConvertFrom(sf) as System.Drawing.Image;
Any insight would be greatly appreciated!
I still don't know the real cause of your problem, but i guess it is related with a image format which Image class doesn't recognize. After inspecting the binary data a little bit, I could be able to form your image. I hope this helps.
Bitmap GetBitmap(byte[] buf)
{
Int16 width = BitConverter.ToInt16(buf, 18);
Int16 height = BitConverter.ToInt16(buf, 22);
Bitmap bitmap = new Bitmap(width, height);
int imageSize = width * height * 4;
int headerSize = BitConverter.ToInt16(buf, 10);
System.Diagnostics.Debug.Assert(imageSize == buf.Length - headerSize);
int offset = headerSize;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
bitmap.SetPixel(x, height - y - 1, Color.FromArgb(buf[offset + 3], buf[offset], buf[offset + 1], buf[offset + 2]));
offset += 4;
}
}
return bitmap;
}
private void Form1_Load(object sender, EventArgs e)
{
using (FileStream f = File.OpenRead("base64.txt"))
{
byte[] buf = Convert.FromBase64String(new StreamReader(f).ReadToEnd());
Bitmap bmp = GetBitmap(buf);
this.ClientSize = new Size(bmp.Width, bmp.Height);
this.BackgroundImage = bmp;
}
}
The posted code seems correct. I have tested it and it works fine.
The exception "System.ArgumentException: Parameter is not valid." without any other hint (especially not the name of the parameter) is a wrapper for GDI+ (the underlying technology behind .NET Image class) standard InvalidParameter error, which does not tell use exactly what parameter is invalid.
So, following the FromStream code with .NET Reflector, we can see that the parameters used in GDI+ calls are essentially ... the input stream.
So my guess is the input stream you provide is sometimes invalid as an image? You should save the failing input streams (using File.SaveAllBytes(sf) for example) for further investigation.
This could happen if sf contained invalid image data. Verify the validity of the data you're passing into the stream, and see if that fixes your issue.
Im currently trying to use writeablebitmap to take a IntPtr of a scan of images and turn each one into a Bitmap. Im wanting to use writeablebitmap because im having an issue with standard gdi
GDI+ System.Drawing.Bitmap gives error Parameter is not valid intermittently
There is a method on a WriteableBitmap that called WritePixels
http://msdn.microsoft.com/en-us/library/aa346817.aspx
Im not sure what I set for the buffer and the stride every example I find it shows the stride as 0 although that throws an error. When I set the stride to 5 the image appear black. I know this may not be the most efficient code but any help would be appreciated.
//create bitmap header
bmi = new BITMAPINFOHEADER();
//create initial rectangle
Int32Rect rect = new Int32Rect(0, 0, 0, 0);
//create duplicate intptr to use while in global lock
dibhand = dibhandp;
bmpptr = GlobalLock(dibhand);
//get the pixel sizes
pixptr = GetPixelInfo(bmpptr);
//create writeable bitmap
var wbitm = new WriteableBitmap(bmprect.Width, bmprect.Height, 96.0, 96.0, System.Windows.Media.PixelFormats.Bgr32, null);
//draw the image
wbitm.WritePixels(rect, dibhandp, 10, 0);
//convert the writeable bitmap to bitmap
var stream = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbitm));
encoder.Save(stream);
byte[] buffer = stream.GetBuffer();
var bitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));
GlobalUnlock(dibhand);
GlobalFree(dibhand);
GlobalFree(dibhandp);
GlobalFree(bmpptr);
dibhand = IntPtr.Zero;
return bitmap;
An efficient way to work on Bitmaps in C# is to pass temporarily in unsafe mode (I know I don't answer the question exactly but I think the OP did not manage to use Bitmap, so this could be a solution anyway). You just have to lock bits and you're done:
unsafe private void GaussianFilter()
{
// Working images
using (Bitmap newImage = new Bitmap(width, height))
{
// Lock bits for performance reason
BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, newImage.Width,
newImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* pointer = (byte*)newImageData.Scan0;
int offset = newImageData.Stride - newImageData.Width * 4;
// Compute gaussian filter on temp image
for (int j = 0; j < InputData.Height - 1; ++j)
{
for (int 0 = 1; i < InputData.Width - 1; ++i)
{
// You browse 4 bytes per 4 bytes
// The 4 bytes are: B G R A
byte blue = pointer[0];
byte green = pointer[1];
byte red = pointer[2];
byte alpha = pointer[3];
// Your business here by setting pointer[i] = ...
// If you don't use alpha don't forget to set it to 255 else your whole image will be black !!
// Go to next pixels
pointer += 4;
}
// Go to next line: do not forget pixel at last and first column
pointer += offset;
}
// Unlock image
newImage.UnlockBits(newImageData);
newImage.Save("D:\temp\OCR_gray_gaussian.tif");
}
}
This is really much more efficient than SetPixel(i, j), you just have to be careful about pointer limits (and not forget to unlock data when you're done).
Now to answer your question about stride: the stride is the length in bytes of a line, it is a multiple of 4. In my exemple I use the format Format32bppArgb which uses 4 bytes per pixel (R, G, B and alpha), so newImageData.Stride and newImageData.Width * 4 are always the same. I use the offset in my loops only to show where it would be necessary.
But if you use another format, for instance Format24bppRgb which uses 3 bytes per pixel (R, G and B only), then there may be an offset between stride and width. For an image 10 * 10 pixels in this format, you will have a stride of 10 * 3 = 30, + 2 to reach nearest multiple of 4, i.e. 32.