Reseize image

rfazendeiro

Centurion
Joined
Mar 8, 2004
Messages
110
Hi to all.

I searched in the forum for information to solve my problema but did not find what i need.

I want to publish some images but i don't want other to use the images without bying them. my ideia is to have a reduced size of the original image with a watermark on it for people to browse.

Now i have woked out how to put the watermark and reduce it's resolution. what i can't seem to do is reseize the image. Does anyone know how i can do this? For example i have images of 1024x740 or 2560x1920 and want to reseize it to 640x480 (it wont be exactly like that because i calculate the porpotion so that the image retains it's look).

how can i do this?
 
A file I haven't touched in 2 yrs. It looks crappy at 1st sight but it does resize jpegs.

Visual Basic:
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Net;

namespace HB.Utility
{
	/// <summary>
	/// Represents a JPEG image.
	/// </summary>
	[Serializable]
	public class Jpeg : IDisposable, ICloneable
	{
		/// <summary>
		/// Loads a JPEG file as an <see cref="Image"/> from a website or a local file.
		/// </summary>
		/// <param name="source">The path of the file.</param>
		/// <exception cref="ArgumentNullException">When parameter source is null.</exception>
		public Jpeg(string source)
		{
			if(source == null)
				throw new ArgumentNullException("source");
			if(source.StartsWith("http://"))
				image = LoadFromWeb(source);
			else
				image = Image.FromFile(source, true);
			compression = 50;
			quality = 100;
			disposed = false;
		}

		/// <summary>
		/// Copy constructor.
		/// </summary>
		/// <param name="jpeg">The <see cref="Jpeg"/> to copy.</param>
		/// <exception cref="ArgumentNullException">When paremeter jpeg is null.</exception>
		/// <remarks>Creates a deep copy of a <see cref="Jpeg"/> object.</remarks>
		public Jpeg(Jpeg jpeg)
		{
			if(jpeg == null)
				throw new ArgumentNullException("jpeg");
			compression = jpeg.Compression;
			disposed = jpeg.disposed;
			image = (Image) image.Clone();
			quality = jpeg.Quality;
		}

		/// <summary>
		/// Gets or sets the compression value of the JPEG.
		/// </summary>
		public long Compression
		{
			get
			{
				IsDisposed();
				return compression;
			}
			set
			{
				IsDisposed();
				if(value < 0)
					throw new InvalidOperationException("Compression cannot be less than zero.");
				compression = value;
			}
		}

		/// <summary>
		/// Gets or sets the quality value of the JPEG.
		/// </summary>
		public long Quality
		{
			get
			{
				IsDisposed();
				return quality;
			}
			set
			{
				IsDisposed();
				if(value < 0)
					throw new InvalidOperationException("Quality cannot be less than zero.");
				quality = value;
			}
		}

		/// <summary>
		/// Gets the <see cref="ImageCodecInfo"/> for JPEG file types.
		/// </summary>
		static Jpeg()
		{
			imageCodecInfo = null;
			ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
			foreach(ImageCodecInfo encoder in encoders)
				if(encoder.MimeType == "image/jpeg")
					imageCodecInfo = encoder;
			if(imageCodecInfo == null)
				throw new Exception("JPEG file format is not supported on this machine.");
		}

		/// <summary>
		/// Resizes an image proportionally by height.
		/// </summary>
		/// <param name="height">The new height of the image.</param>
		/// <returns>The resized image.</returns>
		/// <exception cref="ArgumentOutOfRangeException">When parameter height is less than zero.</exception>
		public void ResizeByHeight(int height)
		{
			IsDisposed();
			if(height < 0)
				throw new ArgumentOutOfRangeException("height", height, "Height cannot be less than zero.");
			float rate = (float) height/image.Height;
			height = (int) (image.Height * rate);
			int width = (int) (image.Width * rate);
			Resize(width, height);
		}

		/// <summary>
		/// Resizes the <see cref="Image"/> of the current instance.
		/// </summary>
		/// <param name="width">The new width of the image.</param>
		/// <param name="height">The new height of the image.</param>
		/// <exception cref="ApplicationException">When an error occurred while trying to resize the image.</exception>
		private void Resize(int width, int height)
		{
			Image target = (Image) new Bitmap(width, height);
			using(System.Drawing.Graphics gThumb = System.Drawing.Graphics.FromImage(target))
			{
				try
				{
					gThumb.DrawImage(image, 0, 0, width, height);			
					image.Dispose();
					image = target;
				}
				catch(Exception e)
				{
					try
					{
						target.Dispose();
					}
					catch
					{
					}
					string msg = "An error occurred while trying to resize the image.";
					throw new ApplicationException(msg, e);
				}
			}
		}

		/// <summary>
		/// Resizes an image proportionally by width.
		/// </summary>
		/// <param name="width">The new width of the image.</param>
		/// <returns>The resized image.</returns>
		/// <exception cref="ArgumentOutOfRangeException">When parameter width is less than zero.</exception>
		public void ResizeByWidth(int width)
		{
			IsDisposed();
			if(width < 0)
				throw new ArgumentOutOfRangeException("width", width, "Width cannot be less than zero.");
			float rate = (float) width/image.Width;
			width = (int) (image.Width * rate);
			int height = (int) (image.Height * rate);
			Resize(width, height);
		}

		/// <summary>
		/// Loads an image from a URL.
		/// </summary>
		/// <param name="url">The URL of the image.</param>
		/// <returns>The downloaded imahe.</returns>
		/// <remarks>No exception handling is performed.</remarks>
		private static Image LoadFromWeb(string url)
		{
			HttpWebResponse res = null;
			try
			{
				HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
				res = (HttpWebResponse) req.GetResponse();
				return Image.FromStream(res.GetResponseStream(), true);
			}
			finally
			{
				try
				{
					res.Close();
				}
				catch{}
			}
		}

		/// <summary>
		/// Saves the <see cref="Jpeg"/>.
		/// </summary>
		/// <param name="filename">The file path to save the <see cref="Jpeg"/> as.</param>
		public void Save(string filename)
		{
			IsDisposed();
			Save(filename, image);
		}

		/// <summary>
		/// Calls <see cref="Dispose"/> if <see cref="Dispose"/> has not already been called.
		/// </summary>
		~Jpeg()
		{
			try
			{
				if(!disposed)
					Dispose();
			}
			catch{}
		}
		
		/// <summary>
		/// Disposes the underlying <see cref="Image"/>.
		/// </summary>
		public void Dispose()
		{
			IsDisposed();
			image.Dispose();
			GC.SuppressFinalize(this);
			disposed = true;
		}

		/// <summary>
		/// Determines if this instance of <see cref="Jpeg"/> has been disposed.
		/// </summary>
		/// <exception cref="ObjectDisposedException">When this instance of <see cref="Jpeg"/>
		/// has been disposed.</exception>
		private void IsDisposed()
		{
			if(disposed)
				throw new ObjectDisposedException(null);
		}

		/// <summary>
		/// Saves an <see cref="Image"/> as a JPEG.
		/// </summary>
		/// <param name="filename">the file path to save the <see cref="Jpeg"/> as.</param>
		/// <param name="image">The <see cref="Image"/> to save.</param>
		private void Save(string filename, Image image)
		{
			EncoderParameters parameters = new EncoderParameters(2);
			parameters.Param[0] =  new EncoderParameter(Encoder.Quality, quality);
			parameters.Param[1] =  new EncoderParameter(Encoder.Compression, compression);
			image.Save(filename, JpegCodecInfo, parameters);
		}

		/// <summary>
		/// Gets the currently loaded image.
		/// </summary>
		public Image LoadedImage
		{
			get
			{
				IsDisposed();
				return image;
			}
		}

		/// <summary>
		/// Gets the JPEG image codec info.
		/// </summary>
		private static ImageCodecInfo JpegCodecInfo
		{
			get
			{
				return imageCodecInfo;
			}
		}
		
		/// <summary>
		/// Gets a deep copy of the invoked <see cref="Jpeg"/>.
		/// </summary>
		/// <returns>A deep copy of the invoked <see cref="Jpeg"/>.</returns>
		/// <remarks>A clone is created using the copy constructor.</remarks>
		public object Clone()
		{
			IsDisposed();
			return new Jpeg(this);
		}

		/// <summary>
		/// Stores whether the object has been disposed or not.
		/// </summary>
		private bool disposed;
		
		/// <summary>
		/// Stores the JPEG <see cref="Image"/>.
		/// </summary>
		private Image image;
		
		/// <summary>
		/// Stores the quality value of the <see cref="Jpeg"/>.
		/// </summary>
		private long quality;
		
		/// <summary>
		/// Stores the compression value of the <see cref="Jpeg"/>.
		/// </summary>
		private long compression;
		
		private static ImageCodecInfo imageCodecInfo;
	}
}
 
well. I have tryied to use the getthumnail method but the image quality is very bad. I will have a look to the scripy that was put here

thx
 
PlausiblyDamp said:
Could you not use the .GetThumbnailImage method to do this? Should do what you require - not sure how good the resultant image quality is though.

I remember that not working correctly when I tried it 2 yrs ago.


MSDN said:
If the Image object contains an embedded thumbnail image, then this method retrieves the embedded thumbnail and scales it to the requested size. If the Image object does not contain an embedded thumbnail image, this method creates a thumbnail image by scaling the main image.

GetThumbnailImage works well when the requested thumbnail image has a size of about 120 x 120. If you request a large thumbnail image (say 300 x 300) from an Image object that has an embedded thumbnail, there could be a noticeable loss of quality in the thumbnail image. It might be better to scale the main image (instead of scaling the embedded thumbnail) by calling DrawImage.
 
One easy method I use to resize (Sorry I only program in VB) is the folowing.

I am not sure if this is good enough for you but I have seen this question pass so many times.

Make sure to invoke the GarbageCollection every now and then Bitmaps can drain memory resources quite fast if invoked a lot of times.

Visual Basic:
Shared Sub main()
        Dim iBmp As Bitmap = Bitmap.FromFile("c:\test.jpg")

        Dim oBMP As Bitmap = New Bitmap(200, 200)

        Dim g As Graphics = Graphics.FromImage(oBMP)
        g.DrawImage(iBmp, 0, 0, oBMP.Height, oBMP.Width)
        oBMP.Save("c:\test1.jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
    End Sub
 
You may get better mileage out of disposing the Bitmap / Graphics objects when finished with them rather than relying on the GC being invoked manually to clean up the resources.
 
Doesn't disposing still keep the bitmap in memory until the GC is called?

I once had a problem with a function that resized 25000 quite large images very quickly one after the another and I remember that disposing of them still consumed too much memory and even crashed the program. So I ended up reusing the bitmaps every cycle and calling the GC every 1000 or so cycles.
 
well i'm using the dispose method to discard the images. How can do i call the GC and how will it hurt the performance ?

what i'm doing is the user selects a directory and i cycle through all images and store in the database 3 copies of it (The images are already ate the server uploaded by an ftp). The original, a reduzed version with a watermark and a thumbnail. I tested with 72 images ocuping 107mb (more ou less 1.55mb for each image) and the script ended after 8min (beside storing the images i have to create some more objects that comes with the tecnology i'm working with).

i came out with this method


Private Function ScaleImage(ByVal bm_source As Bitmap) As Bitmap
Dim rate As Double = CDbl(680 / bm_source.Width)
Dim width As Integer = CInt(bm_source.Width * rate)
Dim height As Integer = CInt(bm_source.Height * rate)

' Make a bitmap for the result.
Dim bm_dest As New Bitmap(width, height)

' Make a Graphics object for the result Bitmap.
Dim gr_dest As Graphics = Graphics.FromImage(bm_dest)

' Copy the source image into the destination bitmap.
gr_dest.DrawImage(bm_source, 0, 0, bm_dest.Width + 1, bm_dest.Height + 1)

gr_dest.Dispose()
bm_source.Dispose()

Return DrawWatermark(bm_dest)
End Function


After i reduze the image i draw a watermark on it (DrawWatermark(bm_dest)).
 
To call the garbage collection just use “GC.collect”

But I would add a static integer that keeps track of the cycles

Static Counter as integer
Counter +=1
If counter = 100 then ‘Can be more
GC.Collect
counter=0
End if

I don't know if it will be needed to do this. You could look in the windows Task manager under Processes if your programs memory consumption is rising as the program runs more cycles. If so you neet to put your garbage out if not don’t worry.
 
yes, i thought of that and looked at task manager. At eache cycle the memory rises and then drops which means that memory is beeing free (without the GC).

thx alot for the help ppl!
 
If you really, really must force a garbage collection then avoid GC.Collect() and try using GC.Collect(0). If you do not specify a parameter then it will do a full memory collection (generation 2), any objects that are not eligible will be promoted a generation and will potentially stay in memory longer.
Generally it is best to leave the memory management to the garbage collector unless you can prove that is the problem. Often just remebering to close / dispose of resources will prevent excesive memory consumption.
 
Back
Top