I have some images that have more than, let's say 6.000.000 pixels and I want to scale them to be somewhere around that value.
public void downscaleByCalculateInSampleSize(string filePath, string newPath)
{
int reqNumberOfPixels = 6000000;
double inSampleSize = 1;
using (System.Drawing.Image oImage = System.Drawing.Image.FromFile(filePath))
{
int newWidth = oImage.Width;
int newHeight = oImage.Height;
int actualNumberofPixels = oImage.Width * oImage.Height;
if (actualNumberofPixels > reqNumberOfPixels)
{
inSampleSize = Math.Sqrt(actualNumberofPixels / reqNumberOfPixels);
newWidth = Convert.ToInt32(Math.Round((float)oImage.Width / inSampleSize));
newHeight = Convert.ToInt32(Math.Round((float)oImage.Height / inSampleSize));
}
var newImage = new Bitmap(newWidth, newHeight);
Graphics graphics = Graphics.FromImage(newImage);
graphics.DrawImage(oImage, 0, 0, newWidth, newHeight);
newImage.Save(newPath);
}
}
I've tried to downscale an image that had 6367 x 4751 pixels and 72 dpi resolution (24 bit depth) with the size of 8.03 MB. I've resized this image and I was expecting to be a much more smaller one in size (bellow 8 MB) but mine has 17. The scaled image is 2847 x 2125 (96 dpi with 32 Bit depth). Why is this happening?
Is there a way to downscale an image to a requested number of pixels and the result to have the size much more smaller? I don't care about the resolution...
You are using integer division and truncating results at:
inSampleSize = Math.Sqrt(actualNumberofPixels / reqNumberOfPixels);
Try instead:
inSampleSize = Math.Sqrt((double)actualNumberofPixels / (double)reqNumberOfPixels);
Also, save with:
newImage.Save(newPath, ImageFormat.Jpeg);
As the sizes you are getting seem much too large if you are saving with a lossy format with that many pixels
Related
I know this is mostly an image question not code, but I'll give it a shot here.
So first I have a 8bits/pixel grayscale image(bitmap). Which means that each pixel is represented into 1
byte. This means that the the pixel value is the byte value. Clear enough.
But then...
I have a 16bits/pixel grayscale image (bitmap). Which means that each pixel is represented into 2 bytes. This is clear for me. Now I create a byte[] array that will hold each byte value.
For an 1024x1024 image I will have 2097152 bytes. It's 1024*1024*2.
My question is now, how do I get the pixel value for a specific pixel.
Let's say for pixel at position(X|Y) the bytes are 84 and 77. How do I transform these 2 values into the pixel value.
Firstly I need this for some calculation where I need the pixel value, then I want to change the color palette of the bitmap and it works fine with 8 bitsperpixel images, but doesn't for 16bitsperpixel images.
Any help would be nice.
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var dpiX = bitmap.DpiX;
var dpiY = bitmap.DpiY;
byte[] pixels = new byte[
bitmap.PixelHeight * bitmap.PixelWidth *
bitmap.Format.BitsPerPixel / 8];
bitmap.CopyPixels(pixels, bitmap.PixelWidth * bitmap.Format.BitsPerPixel / 8, 0);
This is how I create the array of pixels.
It may be easier to use a 2-byte type for the pixel array:
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var pixels = new ushort[width * height];
bitmap.CopyPixels(pixels, 2 * width, 0);
Access an individual pixel value directly from that array:
var x = 100;
var y = 200;
var pixel = (int)pixels[width * y + x];
In order to convert the 16bpp pixel array into a 8bpp array, just divide each pixel value by 256
var pixels8bpp = pixels.Select(p => (byte)(p / 256)).ToArray();
and create a 8bpp BitmapSource by
var bitmap8bpp = BitmapSource.Create(
width, height, 96, 96, PixelFormats.Gray8, null, pixels8bpp, width);
First of all, sorry for the title but I don't know how to explain it better.
Here is my question. I'm scaling images, check for duplicates and watermark them. The interesting thing is that when I save them to a "/temp/" folder, the whole process is faster than just scaling and using them while they're loaded in the memory.
This is the code that I'm using for scaling:
private static Bitmap ScaleImage(string pathToImage, int maxWidth, int maxHeight)
{
Bitmap image = new Bitmap(pathToImage);
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
Bitmap bmp = new Bitmap(newImage);
return bmp;
}
Then I just assign the scaled images to variables:
Image imageLeft = ScaleImage(tempImage1, 48, 48);
Image imageRight = ScaleImage(tempImage2, 48, 48);
... and compare them.
So what bothers me is why this is slower than saving all scaled images in a folder and then do the comparsion?
I am Resizing a Image and add watermark to image. And Return the image. Here is my code.
Image resizedImage = ResizeImage(original6, new Size(500, 375));
Re size function:
public static System.Drawing.Image ResizeImage(System.Drawing.Image image, Size size)
{
int newWidth;
int newHeight;
if (true)
{
int originalWidth = image.Width;
int originalHeight = image.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
System.Drawing.Image newImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
}
System.Drawing.Bitmap bitmapimage = new System.Drawing.Bitmap(newImage, size.Width, size.Height);// create bitmap with same size of Actual image
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmapimage);
SolidBrush brush = new SolidBrush(Color.FromArgb(113, 255, 255, 255));
//Adding watermark text on image
g.DrawString("My watermark", new Font("Arial", 16, FontStyle.Bold), brush, 5, 100);
return bitmapimage;
}
I am retrieve the new re sized image with watermark and going to save as new image file.
resized6.Save(Server.MapPath(sSavePath + ownerRef + "Pic6v2" + ".jpg"));
This is working fine. However I can't control the file size.
When my original JPG is only 45kb but when my new re-sized image is 500kb. How can I reduce the file size.?
info: original resolution (400x300 px) and new image (500x375px)
I don't remember this off the top of my head, but file size generally has to do with JPEG quality settings. You also need to make sure it's saved as an actual jpg, not a bitmap, which I don't see that you're doing..
See also: C# Simple Image Resize : File Size Not Shrinking
JPEG Quality settings:
http://msdn.microsoft.com/en-us/library/bb882583.aspx
You may be able to change the quality of the JPEG to get a smaller file size. See
http://msdn.microsoft.com/en-us/library/bb882583.aspx
also
Quality of a saved JPG in C#
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I get better results when shrinking an image
I am trying to generate a thumbnail of size 200 x 200px using the code below. It works fine for certain images but does not for others. When it does not work, the thumbnail is generated with that size but only partial image is seen and the other part is gray (like you have used a gray brush and smeared on top of the thumbnail). I haven't been able to see a trend when it fails. For example, it fails for a JPEG image with 400px x 400px size. If I try to generate the thumbnail with size 150px x 150px, there is no image loss.
If this helps here is one image that causes problems - http://s11.postimage.org/sse5zhpqr/Drawing_8.jpg
Appreciate your time.
public Bitmap GenerateThumbnail(Bitmap sourceBitmap, int thumbnailWidth, int thumbnailHeight)
{
Bitmap thumbnailBitmap = null;
decimal ratio;
int newWidth = 0;
int newHeight = 0;
// If the image is smaller than the requested thumbnail size just return it
if (sourceBitmap.Width < thumbnailWidth &&
sourceBitmap.Height < thumbnailHeight)
{
newWidth = sourceBitmap.Width;
newHeight = sourceBitmap.Height;
}
else if (sourceBitmap.Width > sourceBitmap.Height)
{
ratio = (decimal)thumbnailWidth / sourceBitmap.Width;
newWidth = thumbnailWidth;
decimal tempDecimalHeight = sourceBitmap.Height * ratio;
newHeight = (int)tempDecimalHeight;
}
else
{
ratio = (decimal)thumbnailHeight / sourceBitmap.Height;
newHeight = thumbnailHeight;
decimal tempDecimalHeight = sourceBitmap.Width * ratio;
newWidth = (int)tempDecimalHeight;
}
thumbnailBitmap = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage(thumbnailBitmap);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.FillRectangle(Brushes.White, 0, 0, newWidth, newHeight);
g.DrawImage(sourceBitmap, 0, 0, newWidth, newHeight);
g.Dispose();
return thumbnailBitmap;
}
Update:
I did some more analysis and it seems that the issue is while saving the thumbnail to the SQL Server database in a varbinary column. I am using a MemoryStream to do that as below. If I save it to the disk it appears just fine. Here is my thumbnail from the database - http://s12.postimage.org/a7j50vr8d/Generated_Thumbnail.jpg
using (MemoryStream thumbnailStream = new MemoryStream())
{
thumbnailBitmap.Save(thumbnailStream, System.Drawing.Imaging.ImageFormat.Jpeg);
return thumbnailStream.ToArray();
}
Update - This problem is resolved. The issue was NHibernate mapping that I was using for the varbinary column. I had to specify the type as "BinaryBlob" to make sure there is no silent truncation of data > 8KB.
I appreciate, this should probably be a comment and not an answer, but for the extra formatting I'm posting as an answer.
Here's the code I've used to shrink an image and I've never had this issue:
private byte[] ResizeImage(System.Drawing.Image image, double scaleFactor)
{
//a holder for the result
int newWidth = (int)(image.Width * scaleFactor);
int newHeight = (int)(image.Height * scaleFactor);
Bitmap result = new Bitmap(newWidth, newHeight);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(result, typeof(byte[]));
}
Granted, I return a byte[] and not a Bitmap, as I then save it to a database.
The only differences I can really see is that instantiate my result Bitmap with a height and width, I have no call to the FillRectangle method, and I do not set the PixelOffsetMode. I also start off with an Image instead of a Bitmap
Hope this helps you somehow though.
I have several problem with creating a bitmap from a array. I have a camera and from this I get grayscale values in ushort format. But how to create a bitmap from this values? Only:
System.Drawing.Bitmap checks = new System.Drawing.Bitmap(10, 10);
.
.
checks.Save(#"C:\test.bmp", ImageFormat.Bmp);
will not work:(. I get an image and can open it with window tools, but when I will open the file with another graphic lib, I get alot of errors. So does anybody now how to create a correct bmp file with header etc? does anybody have some code example? this would help most.
thanks
You should create a Bitmap with the right dimensions (width, height), and use LockBits to get a handle to memory that you should write to. If your data is in a .NET supported PixelFormat, you can pass that to LockBits and simply copy data. If not, you might have to do some data conversion manually.
It all boils down to what format you receive data samples in, but the above description outlines the steps you need to take to generate your image.
Update: Since your data is 16 bits gray scale, there is a PixelFormat you can use directly, PixelFormat.16bppGrayScale.
public class(path,wid,height,boolean)
{
System.Drawing.Image myThumbnail150;
System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
System.Drawing.Image imagesize = System.Drawing.Image.FromFile(pic.FilePath);
using (Bitmap bitmapNew = new Bitmap(imagesize))
{
double maxWidth = Convert.ToDouble(ConfigurationSettings.AppSettings["ImageWidth"]);
double maxHeight = Convert.ToDouble(ConfigurationSettings.AppSettings["ImageHeight"]);
int w = imagesize.Width;
int h = imagesize.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
myThumbnail150 = bitmapNew.GetThumbnailImage((int)newWidth, (int)newHeight, myCallback, IntPtr.Zero);
string name = pic.Name.Replace(Path.GetExtension(pic.Name), ".Bmp");
//Create a new directory name ThumbnailImage
//Save image in TumbnailImage folder
myThumbnail150.Save(yourpath+ name, System.Drawing.Imaging.ImageFormat.Bmp);
bitmapNew.Dispose();
}