When attempting to add image frames using SelectActiveFrame to an image list I get an Out of Memory exception. All works 100% if I process less than 174 pages but anything over this produces this error. In it's simplest form my code is;
var scannedImage = (Bitmap)Image.FromFile(#"C:\Users\rbl\Documents\Modelware\City Prop\TIFF Files\barcodememory.tiff");
var dim = new FrameDimension(scannedImage.FrameDimensionsList[0]);
var imageCount = scannedImage.GetFrameCount(dim);
var currentBatch = new List<Image>();
for (var i = 0; i < imageCount; i++)
{
scannedImage.SelectActiveFrame(dim, i);
currentBatch.Add(new Bitmap(scannedImage));
// Above experiences following error after +/- 174 pages
//System.OutOfMemoryException was unhandled
//Message=Out of memory.
//Source=System.Drawing
}
You are running out of memory. You need to load the images in batches (just load what you need and unload it as you no longer need it.)
Anyways... I don't see why you need several bitmaps. Just do new Bitmap(...) once and reuse it (unless the code you pasted is actually not your real code and you're processing several scannedImage inside that for loop.)
It is interesting that I am able to resolve my problem by converting the frame to a bytestream and then back to an image. The following works 100% (just not sure why, or whether this is a good solution).
for (int i = 0; i < pageCount; i++)
{
image.SelectActiveFrame(dim, i);
var byteStream = new MemoryStream();
image.Save(byteStream, ImageFormat.Bmp);
frames[i] = Image.FromStream(byteStream);
}
Related
I need to extract all image frames from a DICOM SC using fo-DICOM. I have a test app that extracts and displays the images, which works fine. However, I need to save the individual images to a database, and am running into problems.
I have the following code so far:
public void SetImages(DicomFile dicom, ThumbResults results)
{
var images = new DicomImage(dicom.Dataset);
for(var count = 0; count < images.NumberOfFrames; count++)
{
var image = images.RenderImage(count).As<Bitmap>();
using(var stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Jpeg);
results.Images.Add(Convert.ToBase64String(stream.ToArray()));
}
}
}
I get a DicomImagingException, "Cannot cast to 'Bitmap'" on images.RenderImage. It works in my test code, when I call PictureBox.Image = _image.RenderImage(count).As<Bitmap>(); so I figure RenderImage must be specifically for rendering (as the name implies).
How should I go about extracting individual frames to a string that will be saved to the database?
In case someone else runs into this problem, the issue was the original code was in .NET Framework, but the new code was in .NET Core. In Core, the ImageManager does not use the WindowsImageManager by default, so you need to set it manually.
ImageManager.SetImplementation(WinFormsImageManager.Instance);
I am writing a code to write a media (CD/DVD) using IMAPI (C#.NET).
The writing works all fine.
In case when I try to add file which exceeds the free space on media, it throws exception.
I can catch the exception but it does not serve my purpose.
I want to go adding the files even though it exceeds the free space.
Then, I want to return total size of image to user who will then do the needful (update UI, show message, show size of image on UI etc.) as per requirements.
In other words, I want to get total image size even though it exceeds the free space.
I can see lot many media burning applications are doing this; so this must be possible.
Following line in code throws exception: -
fileSystemImage.Root.AddFile(thisFileItem.DestPath + thisFileItem.DisplayName, stream);
Following are the exception details: -
ComException
ErrorCode: -1062555360
Message: Adding 'myfilename' would result in a result image having a size larger than the current configured limit.
Following is my code: -
MsftDiscFormat2Data discFormatData = null;
MsftDiscRecorder2 discRecorder = null;
MsftFileSystemImage fileSystemImage = null;
discRecorder = new MsftDiscRecorder2();
discRecorder.InitializeDiscRecorder(UniqueRecorderId);
discFormatData = new MsftDiscFormat2Data
{
Recorder = discRecorder,
ClientName = CLIENT_NAME,
ForceMediaToBeClosed = _closeSession
};
fileSystemImage = new MsftFileSystemImage();
fileSystemImage.VolumeName = _volumeID;
fileSystemImage.Update += fileSystemImage_Update;
fileSystemImage.ChooseImageDefaults(discRecorder);
fileSystemImage.FileSystemsToCreate = FsiFileSystems.FsiFileSystemJoliet | FsiFileSystems.FsiFileSystemISO9660 | FsiFileSystems.FsiFileSystemUDF;
if(!discFormatData.MediaHeuristicallyBlank)
{
fileSystemImage.MultisessionInterfaces = discFormatData.MultisessionInterfaces;
fileSystemImage.ImportFileSystem();
}
foreach(FileItem thisFileItem in listFileItem)
{
IStream stream = null;
if(thisFileItem.SourcePath != null)
Win32.SHCreateStreamOnFile(thisFileItem.SourcePath, Win32.STGM_READ | Win32.STGM_SHARE_DENY_WRITE, ref stream);
fileSystemImage.Root.AddFile(thisFileItem.DestPath + thisFileItem.DisplayName, stream);
}
IFileSystemImageResult objIFileSystemImageResult;
objIFileSystemImageResult = fileSystemImage.CreateResultImage();
_imageSize = (objIFileSystemImageResult.TotalBlocks * objIFileSystemImageResult.BlockSize);
IStream imageStream = objIFileSystemImageResult.ImageStream;
fileSystemImage.Update -= fileSystemImage_Update;
Not a solution, but i got the hack.
fileSystemImage.FreeMediaBlocks = int.MaxValue;
This will allow to create image of any size (upto integer limit) irrespective of free blocks on media inserted. You can then implement validations in your own way.
I've implemented a class that reads 24 bit-per-pixel TIFF generated by Microsoft.Reporting.WinForms.ReportViewer, converts it to a 1 bit-per-pixel TIFF and stores the result into a file.
This part is working just fine - I'm able to open the resulting TIFF in a TIFF viewer and view the contents.
For compression I'm using the following codec:
outImage.SetField(TiffTag.COMPRESSION, Compression.CCITT_T6);
Now I'm trying to read the same 1 bit-per-pixel TIFF and decompress it. I wrote the following methods:
public static void DecompressTiff(byte[] inputTiffBytes)
{
using (var tiffStream = new MemoryStream(inputTiffBytes))
using (var inImage = Tiff.ClientOpen("in-memory", "r", tiffStream, new TiffStream()))
{
if (inImage == null)
return null;
int totalPages = inImage.NumberOfDirectories();
for (var i = 0; i < totalPages; )
{
if (!inImage.SetDirectory((short) i))
return null;
var decompressedTiff = DecompressTiff(inImage);
...
}
private static byte[] DecompressTiff(Tiff image)
{
// Read in the possibly multiple strips
var stripSize = image.StripSize();
var stripMax = image.NumberOfStrips();
var imageOffset = 0;
int row = 0;
var bufferSize = image.NumberOfStrips() * stripSize;
var buffer = new byte[bufferSize];
int height = 0;
var result = image.GetField(TiffTag.IMAGELENGTH);
if (result != null)
height = result[0].ToInt();
int rowsperstrip = 0;
result = image.GetField(TiffTag.ROWSPERSTRIP);
if (result != null)
rowsperstrip = result[0].ToInt();
if (rowsperstrip > height && rowsperstrip != -1)
rowsperstrip = height;
for (var stripCount = 0; stripCount < stripMax; stripCount++)
{
int countToRead = (row + rowsperstrip > height) ? image.VStripSize(height - row) : stripSize;
var readBytesCount = image.ReadEncodedStrip(stripCount, buffer, imageOffset, countToRead); // Returns -1 for the last strip of the very first page
if (readBytesCount == -1)
return null;
imageOffset += readBytesCount;
row += rowsperstrip;
}
return buffer;
}
The problem is that when ReadEncodedStrip() is called for the last strip of the very first page - it returns -1, indicating that there is an error. And I can't figure out what's wrong even after debugging LibTIFF.NET decoder code. It's something with EOL TIFF marker discovered where it's not expected.
By some reason, LibTIFF.NET can't read a TIFF produced by itself or most likely I'm missing something. Here is the problem TIFF.
Could anyone please help to find the root cause?
After a more than a half day investigation, I've finally managed to detect the cause of this strange issue.
To convert from 24 bit-per-pixel TIFF to 1 bit-per-pixel, I ported algorithms from C to C# of the the 2 tools shipping with original libtiff: tiff2bw and tiffdither.
tiffdither has the bug that it doesn't include last image row in the output image, i.e. if you feed to it an image with 2200 rows height, you get the image with 2199 rows height as output.
I've noticed this bug in the very beginning of the porting and tried to fix, but, as it turned out eventually, not completely and the ported algorithm actually didn't write the last row via WriteScanline() method to the output TIFF. So this was the reason why LibTIFF.NET wasn't able to read last strip\row of the image depending on what reading method I used.
What was surprising to me is that LibTIFF.NET allows to write such actually corrupted TIFF without any error during writing. For example WriteDirectory() method returns true in this situation when image height set via TiffTag.IMAGELENGTH differs from the actual coount of rows written to it. However, later it can't read such the image and the error is thrown while reading.
Maybe this behavior inherited from the original libtiff, though.
I need to get DCT-coefficients array after quantization for further changing bits (steganography).
My question is: Lets say, i have jpeg image in picturebox or whatever. How can i acess to dct coef. of this image using C# and library like LibJpeg.Net? Need a code pls. Can't find anything complete and simple on whole web. Also, can't see any tutorial on LibJpeg.Net.
After this steps:
BitMiracle.LibJpeg.Classic.jpeg_decompress_struct oJpegDecompress = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct();
System.IO.FileStream oFileStreamImage = new System.IO.FileStream(strImagePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
oJpegDecompress.jpeg_stdio_src(oFileStreamImage);
oJpegDecompress.jpeg_read_header(true);
BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] JBlock = oJpegDecompress.jpeg_read_coefficients();
what should i do now, to edit dct coeff? Use .Access()? How do i use this? Any examples?
Following:
short[] block = JBlock[c].Access(x, y);
gives an error like that: "Cannot implicitly convert type 'BitMiracle.LibJpeg.Classic.JBLOCK[][]' to 'short[]'"
Also, while using something similar, it gives an error about converting "BitMiracle.LibJpeg.Classic.JBLOCK[][]" to type "System.IConvertible".
Or maybe someone knows another easy way for my problem?
All right, i figured out something. At least, its answering my main question.
private void button1_Click(object sender, EventArgs e)
{
string path = #"D:\067.jpg";
var img = new Bitmap(path);
var jo = img.Width;
var joj = img.Height;
BitMiracle.LibJpeg.Classic.jpeg_decompress_struct oJpegDecompress = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct();
System.IO.FileStream oFileStreamImage = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
oJpegDecompress.jpeg_stdio_src(oFileStreamImage);
oJpegDecompress.jpeg_read_header(true);
BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] JBlock = oJpegDecompress.jpeg_read_coefficients();
var ll = JBlock[0].Access(0, 1); // accessing the element
var oo = 5; // its gonna be new value for coefficient
for (int i = 0; i < 64; i++) // some cycle
{
ll[0][i][0] = Convert.ToInt16(oo); // changes
}
oJpegDecompress.jpeg_finish_decompress();
oFileStreamImage.Close();
/////
System.IO.FileStream objFileStreamMegaMap = System.IO.File.Create(#"D:\068.jpg");
BitMiracle.LibJpeg.Classic.jpeg_compress_struct oJpegCompress = new BitMiracle.LibJpeg.Classic.jpeg_compress_struct();
oJpegCompress.jpeg_stdio_dest(objFileStreamMegaMap);
oJpegDecompress.jpeg_copy_critical_parameters(oJpegCompress);
oJpegCompress.Image_height = joj;
oJpegCompress.Image_width = jo;
oJpegCompress.jpeg_write_coefficients(JBlock);
oJpegCompress.jpeg_finish_compress();
objFileStreamMegaMap.Close();
oJpegDecompress.jpeg_abort_decompress();
oFileStreamImage.Close();
}
Little bit sloppy, but still, just the test...
Used some code from here
Like that, as you can see in your console, every 0th element under 0->m_buffer->0->i in the output image will be equal to 5
All hail to me.
I've done this while writing a JPEG library to verify its correctness. You just have to get the source code to LIBJPEG; identify where it does the functions you are interested int (somewhat difficult because the code is convoluted); set a breakpoint or return there.
I'm having to fix a bug in some very old code that converts a Base64 string to a image using Memory Stream. Basically it loops through a list of images stored as base64 strings and converts them to images then draws them using ActiveReports.
The bug is that once it loads one image all following images will be a copy of the first image.
I found the code that is doing the conversion of string to image and immediately noticed it isn't disposing of the memory stream. If I wrap the memory stream in a using block I get a GDI exception. I'm guessing this is because the image isn't really read from memory yet or something, but I'd like to hear if anyone has a guess. Thanks in advance!
byte[] oGraphic = null;
try
{
oGraphic = Convert.FromBase64String(psGraphic);
DataDynamics.ActiveReports.Picture oImg = new Picture();
oImg.Top = this.Legend.Top + this.fTopFirst;
oImg.Visible = true;
oImg.Name = sLabelName;
oImg.PictureAlignment = PictureAlignment.Center;
oImg.Image = null;
if (oGraphic != null)
{
var oStream = new MemoryStream(oGraphic);
oImg.Image = System.Drawing.Image.FromStream(oStream);
oImg.Height = Convert.ToSingle(oImg.Image.Height)/(oImg.Image.VerticalResolution);
oImg.Width = Convert.ToSingle(oImg.Image.Width)/(oImg.Image.HorizontalResolution);
oImg.SizeMode = SizeModes.Zoom;
this.fGraphicHeight = oImg.Height;
this.fGraphicWidth = oImg.Width;
if (this.fConstantGraphic > this.fGraphicWidth)
oImg.Left = this.Legend.Left + this.fLeftFirst +
((this.fConstantGraphic - this.fGraphicWidth)/2);
else
oImg.Left = this.Legend.Left + this.fLeftFirst;
}
else
{
this.fGraphicHeight = 0f;
this.fGraphicWidth = 0f;
}
this.GHMap.Controls.Add(oImg);
}
catch (Exception oE)
{
.....
}
The only thing I can imagine is if there is a line of code missing from what you have here:
if (oGraphic == null) // missing line
oGraphic = Convert.FromBase64String(psGraphic);
There is no reason for this byte[] to be declared outside of the try { } block. That array gets wrapped into a MemoryStream which is then wrapped into an Image. That image is attached to a brand new Picture which is added to a Picture collection.
What is it we don't see?
Here's another guess (I'll leave the first guess alone for posterity):
I'm not familiar with Active Reports, but it looks like you're setting the Top and PictureAlignment properties of the Picture object to the same value and adding more than one Picture. Is it possible they are all there, but one on top of each other? So the result is a single picture?
Guess #3 (one of these is going to get a checkmark, I just know it!)
Everything looks OK in the code provided, therefore the problem is somewhere else (though it's still entirely possible I'm wrong about it being OK).
Are you certain that psGraphic is different each time this code is executed?
The cause of the problem is that the Picture control is a single control instance on a single section. So you're just overwriting the image on this single control over and over.
If the only thing you want to see in this report is the images, then the best thing to do is use ActiveReports' Unbound mode and treat each image as another "record". See this walkthrough for an example of using unbound mode (see the DataInitialize and FetchData events for the meat of the matter).
Using unbound mode, ActiveReports will render the image one after the other in sections treating each image like a new record. The code would be something like the following (sorry I don't have ActiveReports handy at the moment so I can't check this code, but this should be pretty close. Let me know if you run into any problems and I'll clean it up in the morning):
In ActiveReports' ReportStart Event:
DataDynamics.ActiveReports.Picture oImg = new Picture();
oImg.Top = 0;
oImg.Visible = true;
oImg.Name = sLabelName;
oImg.PictureAlignment = PictureAlignment.Center;
// setting DataField "binds" the Picture control to get it's data from the MyImageField field which we'll initialize and bind in the events below
oImg.DataField = "MyImageField";
this.Sections["Detail"].Controls.Add(oImg);
In ActiveReports' DataInitialize Event:
this.Fields.Add("MyImageField");
In ActiveReports FetchData Event:
var imageBytes = Convert.FromBase64String(_imageStrings.Current); // I'm not sure where the base64 image strings come from, some I'm assuming you can put them in an enumerator field in the report like "_imageStrings"
var imageStream = new MemoryStream(imageBytes);
var image = Image.FromStream(imageStream);
Fields["MyImageField"].Value = image;
// This tells ActiveReports if there are more records, and if it should raise the FetchData event again (allowing you to add another image).
eArgs.EOF = !_imageStrings.MoveNext();
If you need to resize the image control for each image, use the section's Format event for that. You could use something like the following:
In Detail_Format Event:
var pictureControl = this.Sections["Detail"].Controls["MyImageControl"] as DataDynamics.ActiveReports.Picture;
pictureControl.Width = Convert.ToSingle(pictureControl.Image.Width)/(pictureControl.Image.VerticalResolution);
pictureControl.Width = Convert.ToSingle(pictureControl.Image.Width)/(pictureControl.Image.HorizontalResolution);
Finally, ActiveReports will also just automatically bind to a set of POCO objects in an IEnumerable (or IList, I forget). So you could simply have a "MyImage" class with a property like "MyImage" and ActiveReports will read it and bind with it (you wouldn't have to write any code in DataInitialize and FetchData). I think you might also just be able to put the MemoryStream in there as the binding too and ActiveReports will read it, but I'm not positive on that.
BTW: The reason that GDI exception occurs when disposing the MemoryStream is because GDI attempts to just seek within that single MemoryStream for the image data rather than making a copy of it. So you'll need to supply each System.Drawing.Image instance with a new stream (don't worry MemoryStream will clean itself up when everything is released).
It turned out to be a problem with the way we were creating the image. Adding the code below fixed the problem.
oImg.Image = System.Drawing.Image.FromStream(oStream);
TO THIS
oImg.Image = ImageFromBase64String(psGraphic);
private Image ImageFromBase64String(string sBase64String)
{
using (var sStream = new MemoryStream(Convert.FromBase64String(sBase64String)))
using (var iSourceImage = Image.FromStream(sStream))
{
return new Bitmap(iSourceImage);
}
}