I have the following code to rotate an image in C#:
using (var stream = new FileStream(path, FileMode.Open))
{
using (var image = Image.FromStream(stream))
{
stream.Close();
image.RotateFlip(rotateType);
image.Save(path1, ImageFormat.Png);
image.Dispose();
}
}
If the original file size was 700 KiB, the new rotated file has size of 7+ MiB.
What is wrong with this code? Any help is much appreciated.
Update:
I tried changing the line image.Save(path1, ImageFormat.Png) to image.Save(path1) and image.Save(path1, image.RawFormt) with no improvement.
C# - How to change PNG quality or color depth
This guy's question looks similar to the same thing you are seeing.
PNG is a bitmap file format:
higher filesize compared to jpg
Because of this you should safe your image as jpg:
Thus lossless PNG format is best suited for pictures still under edition - and the lossy formats, like JPEG, are best for the final distribution of photographic images, because in this case JPG files are usually smaller [...]
Source: wikipedia
Try safing the image in JPEG via:
image.Save(path, YourClass.GetImageFormat(image));
Tests:
Rotating an JPG file with this method and the size stays the same.
Rotating a 15.7MiB BMP file, the new size is ~800kiB.
To use the existing file format, use this extension method:
public static System.Drawing.Imaging.ImageFormat GetImageFormat(System.Drawing.Image img)
{
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
return System.Drawing.Imaging.ImageFormat.Jpeg;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Bmp))
return System.Drawing.Imaging.ImageFormat.Bmp;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Png))
return System.Drawing.Imaging.ImageFormat.Png;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Emf))
return System.Drawing.Imaging.ImageFormat.Emf;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Exif))
return System.Drawing.Imaging.ImageFormat.Exif;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Gif))
return System.Drawing.Imaging.ImageFormat.Gif;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Icon))
return System.Drawing.Imaging.ImageFormat.Icon;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.MemoryBmp))
return System.Drawing.Imaging.ImageFormat.MemoryBmp;
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Tiff))
return System.Drawing.Imaging.ImageFormat.Tiff;
else
return System.Drawing.Imaging.ImageFormat.Wmf;
}
Source: StackOverflow
Remember you have to look for the format before you manipulate the image.
Otherwise the image will be recognised as MemoryBmp.
using (var stream = new FileStream(path, FileMode.Open))
{
using (var image = Image.FromStream(stream))
{
stream.Close();
var format = YourClass.GetImageFormat(image);
image.RotateFlip(RotateFlipType.Rotate180FlipNone);
image.Save(path, format);
image.Dispose();
}
}
Related
I am trying to create a linear DNG file based on JPG using Lossy JPEG (34892) compression (which was introduced in DNG 1.4.0.0). After running the method below I got an error "Error: File format is invalid (JPEG dimensions do not match tile)" from dng_validate.exe.
It would be nice if someone can resolve it by adjusting my code. Also, I will be happy for a tutorial link. Using of image tools (magick, dcraw, exiftool) also could be a solution.
public static void LinearRawDng()
{
string file = "eva.jpg";
using (Tiff tif = Tiff.Open("eva-linear.dng", "w"))
{
Image img = Image.FromFile(file);
byte[] raster;
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Jpeg);
raster = ms.ToArray();
}
tif.SetField(TiffTag.MAKE, "Fitler");
tif.SetField(TiffTag.UNIQUECAMERAMODEL, "Fitler");
tif.SetField(TiffTag.DNGVERSION, "\x1\x4\x0\x0");
tif.SetField(TiffTag.DNGBACKWARDVERSION, "\x1\x4\x0\x0");
tif.SetField(TiffTag.SUBFILETYPE, 0);
tif.SetField(TiffTag.PHOTOMETRIC, 34892);
tif.SetField(TiffTag.COMPRESSION, 34892);
tif.SetField(TiffTag.SAMPLESPERPIXEL, 1);
tif.SetField(TiffTag.BITSPERSAMPLE, 8);
tif.SetField(TiffTag.ORIENTATION, Orientation.TOPLEFT);
tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
tif.SetField(TiffTag.IMAGEWIDTH, img.Width);
tif.SetField(TiffTag.IMAGELENGTH, img.Height);
var result = tif.WriteRawStrip(0, raster, raster.Length);
tif.Close();
Console.WriteLine("Finished");
}
}
I am trying to save image date to physical file
Below is image data which I got from a jpeg image (via some browser response):
data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD/.....blah blah .....//2Q==
Below is the code I am using to save image data string to Image
Image image = LoadImage(dataURL);
image.Save(saveLocation);
image.Dispose();
public Image LoadImage(string imageString)
{
imageString = imageString.Substring(imageString.IndexOf(',') + 1);
byte[] bytes = Convert.FromBase64String(imageString);
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
return image;
}
Getting the following exception at image.Save(saveLocation); line
A generic error occurred in GDI+.
I have no issues when the image source with png images with the same code but with jpeg images no matter what the size of jpeg is I am getting that exception every time.
I am able to save the png images with same code at same location.
Edit: Please note that I am getting just data image string from browser (which I am capturing via clipboard of browser) i.e I am not getting bytes from client.
Is there any limit for the bytes which can be converted to image stream and then save back to physical file?
Is there any other approach to do the same?
There doesn't seem to be any reason to go through Image at all here.
Assuming the posted data is valid, just write the content to a file directly:
SaveImage(dataURL, saveLocation);
public bool SaveImage(string imageString, string location)
{
try {
imageString = imageString.Substring(imageString.IndexOf(',') + 1);
byte[] bytes = Convert.FromBase64String(imageString);
using (FileStream fs = new FileStream(location, FileMode.Create))
{
fs.Write(bytes, 0, bytes.Count);
}
}
catch(Exception)
{
return false;
}
return true;
}
I've a set of images that I'm programmatically drawing a simple watermark on them using System.Windows and System.Windows.Media.Imaging (yes, not with GDI+) by following a tutorial in here.
Most of the images are not more than 500Kb, but after applying a simple watermark, which is a text with a transparent background, the image size is drastically increasing.
For example, a 440Kb image is becoming 8.33MB after applying the watermark with the below method, and that is shocking me.
private static BitmapFrame ApplyWatermark(BitmapFrame image, string waterMarkText) {
const int x = 5;
var y = image.Height - 20;
var targetVisual = new DrawingVisual();
var targetContext = targetVisual.RenderOpen();
var brush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#FFFFFF"));
brush.Opacity = 0.5;
targetContext.DrawImage(image, new Rect(0, 0, image.Width, image.Height));
targetContext.DrawRectangle(brush, new Pen(), new Rect(0, y, image.Width, 20));
targetContext.DrawText(new FormattedText(waterMarkText, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
new Typeface("Batang"), 13, Brushes.Black), new Point(x, y));
targetContext.Close();
var target = new RenderTargetBitmap((int)image.Width, (int)image.Height, 96, 96, PixelFormats.Default);
target.Render(targetVisual);
var targetFrame = BitmapFrame.Create(target);
return targetFrame;
}
I've noticed that the image quality is improved compared than the original image. The image is more smoother and colors are more lighter. But, you know I don't really want this. I want the image to be as it is, but include the watermark. No quality increases, and of course no drastic changes in image size.
Is there any settings that I'm missing in here to tell my program to keep the quality as same as source image? How can I prevent the significant change of the image size after the changes in my ApplyWatermark method?
Edit
1. This is how I convert BitmapFrame to Stream. Then I use that Stream to save the image to AmazonS3
private Stream EncodeBitmap(BitmapFrame image) {
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(image));
var memoryStream = new MemoryStream();
enc.Save(memoryStream);
return memoryStream;
}
2. This is how I get the BitmapFrame from Stream
private static BitmapFrame ReadBitmapFrame(Stream stream) {
var photoDecoder = BitmapDecoder.Create(
stream,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.None);
return photoDecoder.Frames[0];
}
3. This is how I read the file from local directory
public Stream FindFileInLocalImageDir() {
try {
var path = #"D:\Some\Path\Image.png";
return !File.Exists(path) ? null : File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
} catch (Exception) {
return null;
}
}
The problem is that when you edit the image, the compression is gone. A 730x1108 JPG with 433kB disc size with 32bit (you mentioned transparency, so ARGB) will need at least 730 * 1108 * 4 = 3,09MB on disc. Of course you can compress it afterwards again (for disc, network stream of what else).
This is the reason why image software always needs much memory even when working with compressed data.
Conclusion: You will need the free memory to work with the image. Not possible to have it otherwise completly at hand.
The reason I asked my question in the comments earlier, is because I noticed there were several different encoders available. A bitmap usually has a significantly larger file size, due to the amount of information it's storing about your image.
I haven't tested this myself, but have you tried a different encoder?
var pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(ApplyWatermark(null, null));
MemoryStream stm = File.Create(image);
pngEncoder.Save(stm);
return stm;
I have the following codes to convert an image(bitmap) to byte array:
public byte[] ConvertImageToByteArray(Image imageToConvert, ImageFormat formatOfImage)
{
byte[] Ret;
try
{
using (MemoryStream ms = new MemoryStream())
{
imageToConvert.Save(ms, formatOfImage);
Ret = ms.ToArray();
}
}
catch (Exception)
{
throw;
}
return Ret;
}
and Convert byte array back to image(bitmap):
public Bitmap ConvertByteArrayToImage(byte[] myByteArray)
{
Image newImage;
using (MemoryStream ms = new MemoryStream(myByteArray, 0, myByteArray.Length))
{
ms.Write(myByteArray, 0, myByteArray.Length);
newImage = Image.FromStream(ms, true);
}
return newImage;
}
Here's my Main Program:
byte[] test = ConvertImageToByteArray(Image.FromFile("oldImage.bmp"), ImageFormat.Bmp);
Bitmap bmp = ConvertByteArrayToImage(test);
bmp.Save("newImage.bmp");
But when I compare both of the image files(old & new bitmap images), their checksum appeared to be different. Any reason for that happening? How to fix it to maintain its integrity?
Basically, there are many ways an identical image can be encoded in a BMP file. If I try your example on a random image I found, I see the .NET Bitmap class saves the file without filling the biSizeImage field in the BITMAPINFOHEADER structure in the BMP header (but the original image produced by IrfanView has it filled), which is a completely correct and documented possibility. (“This may be set to zero for BI_RGB bitmaps.”)
And this is definitely not the only variable thing in the BMP format. For instance, there are multiple possible orderings of pixel data in the image (top-to-bottom, bottom-to-top), specified in the header. (“If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.”)
So, if you receive any BMP file from a source not under your control and really need to produce an image using exactly the same BMP variant, you have a lot work to do, and I don’t think you could use the standard .NET helper classes for that.
See also this question: Save bitmap to file has zero in image size field
After chatting a bit, you solution comes down to reading and writing bytes, take the image object out the equation and just deal with the raw bytes.
To read the file:
MemoryStream ms = new MemoryStream(File.ReadAllBytes("filename"));
To write the file:
File.WriteAllBytes("outputfile", ms.ToArray());
I have a TIFF file with two pages. When I convert the file to JPG format I lose the second page. Is there any way to put two images from a TIFF file into one JPG file?
Because TIFF files are too big, I have to decrease their sizes. Is there any way to decrease TIFF size programmatically? That could also help solve my problem.
Since a TIFF can contain multiple frames but JPG can't, you need to convert each single frame into a JPG.
Taken from Windows Dev Center Samples:
public static string[] ConvertTiffToJpeg(string fileName)
{
using (Image imageFile = Image.FromFile(fileName))
{
FrameDimension frameDimensions = new FrameDimension(
imageFile.FrameDimensionsList[0]);
// Gets the number of pages from the tiff image (if multipage)
int frameNum = imageFile.GetFrameCount(frameDimensions);
string[] jpegPaths = new string[frameNum];
for (int frame = 0; frame < frameNum; frame++)
{
// Selects one frame at a time and save as jpeg.
imageFile.SelectActiveFrame(frameDimensions, frame);
using (Bitmap bmp = new Bitmap(imageFile))
{
jpegPaths[frame] = String.Format("{0}\\{1}{2}.jpg",
Path.GetDirectoryName(fileName),
Path.GetFileNameWithoutExtension(fileName),
frame);
bmp.Save(jpegPaths[frame], ImageFormat.Jpeg);
}
}
return jpegPaths;
}
}
using System.Drawing;
using System.Drawing.Imaging;
Bitmap bm=Bitmap.FromFile("photo.tif");
bm.Save("photo.jpg",ImageFormat.Jpeg);
public static class ConvertTiffToJpeg
{
static string base64String = null;
public static string ImageToBase64(string tifpath)
{
string path = tifpath;
using (System.Drawing.Image image = System.Drawing.Image.FromFile(path))
{
using (MemoryStream m = new MemoryStream())
{
image.Save(m, ImageFormat.Jpeg);
byte[] imageBytes = m.ToArray();
base64String = Convert.ToBase64String(imageBytes);
return base64String;
}
}
}
}
< img src="data:image/jpeg;base64, #ConvertTiffToJpeg.ImageToBase64(#"c:\sample.tif")"/>
c# .net tiff-to-jpeg tiff
We faced some problems when converting TIF files to JPEG, because TIF format supports some types of compressions that are not supported in free toolkits.
I searched the internet and tried some commercial toolkits, but most of them are hard to implement with many limitations. The toolkit that drew my attention is leadtools, because it supports loading and saving many file formats (including TIF images with different compressions). We used this toolkit convert our images to JPEG format. You can find more information in the following page:
http://support.leadtools.com/CS/forums/8925/ShowPost.aspx
Note that you can convert any VB.Net code to C# by using this free code converter:
http://www.developerfusion.com/tools/convert/vb-to-csharp/