Using .Net how do I replace the first page of a multiple page tiff file with a new image. Preferable without create a new file.
I think you could not do it without creating another file.
You could read all the images first, replace the image you want to replace, close the original source, and then replace the file with the new multi-page TIFF, but I believe it will use a lot of memory. I would read an image at a time, and write it to a new file and, as the last step, change the file names.
Something like:
// open a multi page tiff using a Stream
using(Stream stream = // your favorite stream depending if you have in memory or from file.)
{
Bitmap bmp = new Bitmap(imagePath);
int frameCount = bmp.GetFrameCount(FrameDimension.Page);
// for a reference for creating a new multi page tiff see:
// http://www.bobpowell.net/generating_multipage_tiffs.htm
// Here all the stuff of the Encoders, and all that stuff.
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
Image newTiff = theNewFirstImage;
for(int i=0; i<frameCount; i++)
{
if(i==0)
{
// Just save the new image instead of the first one.
newTiff.Save(newFileName, imageCodecInfo, Encoder);
}
else
{
Bitmap newPage = bmp.SelectActiveFrame(FrameDimension.Page);
newTiff.SaveAdd(newPage, ep);
}
}
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.Flush);
newTiff.SaveAdd(ep);
}
// close all files and Streams and the do original file delete, and newFile change name...
Hope it helps. For questions in .NET imaging, the Bob Powell page has a lot of good stuff.
This is fairly easy to do. There's source code on this CodeProject tutorial page that will help you do what you want.
Essentially, you'll need to make a call to Image.GetFrameCount() which will give you the number of images in your multi-page TIFF (just to confirm that you actually have a multi-page TIFF).
You might need to experiment with how you save your resulting TIFF - you may need to reassemble the TIFF manually, or you might be able to edit/replace the image directly before writing the TIFF back to disk.
Related
I have a video file that's already loaded in memory (an IFormFile that's converted to a byte[]).
I've been trying to figure out how to take that video and get a thumbnail image from it, without having to read/write to disk.
I found use cases for MediaToolkit and FFmpeg here, and Movie Thumbnailer here, but from what I can find, those require the video already be saved to disk and have write access to output the thumbnail to a file.
Is there any way I can take either an IFormFile or byte[] and do something similar to what MediaToolkit is doing while being able to keep the result in memory?
I know a lot of folks are saying byte[] isn't the way to go. In that case, I'm more than happy to convert from IFormFile to a Stream, but I still need a way to do that and keep it in memory.
please try this with Windows.Media.Editing should work, not fully tested..
int frameHeight; // you can set the height
int frameWidth; // ...
TimeSpan getFrameInTime = new TimeSpan(0, 0, 1);
//Using Windows.Media.Editing get your ImageStream
var yourClip = await MediaClip.CreateFromFileAsync(someFile);
var composition = new MediaComposition();
// add the section of the video as needed
composition.Clips.Add(yourClip);
// Answer - now that your have your imageStream, get the thumbnail
var yourImageStream = await composition.GetThumbnailAsync(
getFrameInTime,
Convert.ToInt32(frameWidth),
Convert.ToInt32(frameHeight),
VideoFramePrecision.NearestFrame);
//now create your thumbnail bitmap
var yourThumbnailBitmap = new WriteableBitmap(
Convert.ToInt32(frameWidth),
Convert.ToInt32(frameHeight)
);
yourThumbnailBitmap.SetSource(yourImageStream);
Our users needed the ability to change images (rotate\insert scan between pages etc) so I wrote an app that allows them to load a Tiff image and edit as needed.
The problem I have is that when the image is saved it is saved as black and white and not in color. The loaded image displays in color no problem so it seems that it is the saving that is causing the issue.
This is the code I am using to save the Tiff image from an array of BitmapSource:
//create encoder
TiffBitmapEncoder tiffEncoder = new TiffBitmapEncoder();
tiffEncoder.Compression = TiffCompressOption.Ccitt4;
//loop through images and create frames
foreach (BitmapSource src in bitmapList)
{
BitmapFrame bmFrame = BitmapFrame.Create(src);
tiffEncoder.Frames.Add(bmFrame);
}
//create stream to write file
FileStream fStream = null;
try
{
fStream = new FileStream(path, FileMode.Create);
//Save Tiff image to disc
tiffEncoder.Save(fStream);
}
finally
{
if (fStream != null)
{
fStream.Close();
}
}
When I look at the properties of the original file it has a bit depth of 24 but the newly saved image has a bit depth of 1 (black and white?), is this the problem?
I am new to working with Tiffs so any guidance would be greatly appreciated.
Please let me know if you need any other info.
Tx
The images are probably converted to black/white, because of the TiffCompressOption.Ccitt4 compression in use. The CCITT compressions are intended for fax documents, and only supports black/white images.
Instead, use a more general purpose compression, like LZW or Deflate. This will let the image data pass unchanged as 24 or 32 bit color data.
I did see "PDF to Image using GhostScript. No image file has to be created", but that only (sort of) answered half my question. Is it possible to use GhostScriptSharp (or the regular GhostScript dll) to convert a pdf in a MemoryStream to a jpg in a MemoryStream? I speak of a dynamically filled in pdf form with iTextSharp which I am already directing to a MemoryStream to save to a database or stream to a http response, and I'd really love to avoid saving to a file (and subsequent cleanup) if I can.
The sole answer in the answer I referenced claimed that one has to go down to the GhostScript dll to do the latter part, but it was obvious I would need to do a good bit of leg-work to figure out what that meant. Does anyone have a good resource that could help me on this journey?
The thing is that the PDF language, unlike the PostScript language, inherently requires random access to the file. If you provide PDF directly to Standard Input or via PIPE, Ghostscript will copy it to a temporary file before interpreting the PDF. So, there is no point of passing PDF as MemoryStream (or byte array) as it will anyway end up on the disk before it is interpreted.
Take a look at the Ghostscript.NET and it's GhostscriptRasterizer sample for the 'in-memory' output.
Ghostscript.Net is a wrapper to the Ghostscript dll. It now can take a stream object and can return an image that can be saved to an stream. Here is an example that I used on as ASP page to generate PDF's from a memory stream. I haven't completely figured out the best way to handle the ghostscript dll and where to locate it on the server.
void PDFToImage(MemoryStream inputMS, int dpi)
{
GhostscriptRasterizer rasterizer = null;
GhostscriptVersionInfo version = new GhostscriptVersionInfo(
new Version(0, 0, 0), #"C:\PathToDll\gsdll32.dll",
string.Empty, GhostscriptLicense.GPL);
using (rasterizer = new GhostscriptRasterizer())
{
rasterizer.Open(inputMS, version, false);
for (int i = 1; i <= rasterizer.PageCount; i++)
{
using (MemoryStream ms = new MemoryStream())
{
Image img = rasterizer.GetPage(dpi, dpi, i);
img.Save(ms, ImageFormat.Jpeg);
ms.Close();
AspImage newPage = new AspImage();
newPage.ImageUrl = "data:image/png;base64," + Convert.ToBase64String((byte[])ms.ToArray());
Document1Image.Controls.Add(newPage);
}
}
rasterizer.Close();
}
}
I have a functioning application in c#/.net that currently accepts raw image data in a bayer format from a set of embedded cameras and converts them to jpeg images. To save transmission time, I have modified the embedded devices to encode the images as jpegs prior to transmission. I'm an experienced embedded programmer but a total c#/.net noob. I have managed to modify the application to save the arrays to file with a jpeg name using this snippet: ( the offset of 5 is to skip header data in the transmission frame)
FileStream stream = File.Create(fileName);
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(multiBuff.msgData, 5, multiBuff.dataSize - 5);
writer.Close();
The files open up fine, but now I want to treat the data as a bitmap without having to save & load from file. I tried the following on the data array:
MemoryStream stream = new MemoryStream(data);
BinaryReader reader = new BinaryReader(stream);
byte[] headerData = reader.ReadBytes(5);
Bitmap bmpImage = new Bitmap(stream);
But this throws a parameter not valid exception. As a newbie, I'm a little overwhelmed with all the classes and methods for images and it seems like what I'm doing should be commonplace, but I can't find any examples in the usual places. Any ideas?
I think you are looking for Bitmap.FromStream() :
Bitmap bmpImage = (Bitmap)Bitmap.FromStream(stream);
Actually using new Bitmap(stream) should have worked as well - this means that the data in the stream does not constitute a valid image - are you sure the jpg is valid? Can you save it to disk and open it i.e. in Paint to test?
You use the Image class.
Image image;
using (MemoryStream stream = new MemoryStream(data))
{
image = Image.FromStream(stream);
}
FYI it didn't work because reader.ReadBytes(5) returns the 5 first bytes of stream not the bytes after position 5
I am embedding images into my assembly using .resx files. Upon runtime, I need to save the images into standalone files, restoring the original content. How can I extract the original file stream from an System.Drawing.Bitmap instance? I know I can create a stream using Bitmap.Save(), but this transcodes (and in effect - inflates) the images, even when saving a PNG back as PNG.
Or perhaps my mistake is reading them from Resource as Bitmap in the first place?
Yes, once you have read it as a Bitmap object, you can't get the original file stream back. The Bitmap object only contains the uncompressed data, not the original data.
You should read the resource as byte data intead. You can read from the resource stream and write to a file:
using (Stream source = this.GetType().Assembly.GetManifestResourceStream("WindowsApplication1.filename.jpg")) {
using (FileStream dest = File.Create(fileName)) {
byte[] buffer = new byte[4096];
while (true) {
int len = source.Read(buffer, 0, buffer.Length);
if (len == 0) break;
dest.Write(buffer, 0, len);
}
}
}
Note: this requires adding the image as an embedded resource, not a managed resource. You can create an Image from it using Image.FromStream().
I don't get the problem. PNG is a loss-less format, you'll get the exact same image back. Yes, you don't necessarily get the exact same bytes when you Save(). It is a compressed format and the amount of time spent by the compressor on getting the best balance between compression and speed might not be the same. But, so what?
If it is a real problem then you shouldn't add the image as a managed resource. You can add it to your project as an embedded resource and read it from the metadata with Assembly.GetManifestResourceStream(). You'll get the raw image file bytes. Convertible to an Image with Image.FromStream().
HOW TO: Load a Bitmap or Image from a Resource by Using Visual C#
If you're not actually using the Bitmap for display purposes in your appliction (i.e. you're just extracting the resource as a bitmap so you can save it as a file), then the simplest approach is to just get the resource as a Stream and save it to a file.
This tutorial shows how to get resources as streams:
http://devhood.com/Tutorials/tutorial_details.aspx?tutorial_id=75
In the sample, the author gets a stream and passes it to the Bitmap.FromStream method. What you would do instead is just save that stream directly to a file (bypassing Bitmap entirely).
It turns out the problem was in using managed resources - resx builder creates Bitmap objects from image files and serializes those objects, it does not store the original file stream. However, embedded resources can store flat binary files.
I am now embedding all files from my images folder using the following .csproj entries:
<EmbeddedResource Include="Images\*.jpg" />
<EmbeddedResource Include="Images\*.png" />
<EmbeddedResource Include="Images\*.gif" />
Visual Studio shows them as normal in-project files with Build Action set to Embedded Resource.
Then, from my code, I load the original file streams with:
var assembly = GetType().Assembly;
var regex = new Regex("\\.(png|jpg|gif))$");
foreach (var bitmap in assembly.GetManifestResourceNames())
{
if(!regex.IsMatch(bitmap)
continue;
var stream = assembly.GetManifestResourceStream(bitmap);
//handle the stream here
}