I let users upload a banner with a minimum width of 400px. This image will then get a wide of 1110px.
I try to upload images with the following sizes: 960x390, 410x410, 784x250.
When i upload 784x250, the image get the same size 784x250 not the width, 1110px.
I use this:
int Height;
using (var Bmp = new Bitmap(str))
{
if (Bmp.Width < 400)
{
throw;
}
if (Bmp.Height < 150)
{
throw;
}
Height = Bmp.Height;
}
if (Height > 300)
{
Height = 300;
}
str.Position = 0;
var ImageData = str.StreamToByteArray();
var Settings = "width=1110;height="+ Height + ";format=jpg;mode=crop;"
var Setting = new ResizeSettings(Settings);
using (var Out = new MemoryStream())
{
using (var In = new MemoryStream(ImageData))
{
In.Seek(0, SeekOrigin.Begin);
var I = new ImageJob(In, Out, Setting)
{
CreateParentDirectory = false
};
I.Build();
}
Out.Position = 0;
// Upload to blob
}
(str contains a stream with the image)
I want the image to be 1110px wide and max 300px high.
Whats wrong?
ImageResizer does not upscale images unless you specifically ask for it via scale=both or scale=canvas. Upscaling is typically undesired.
Also, you can pass the stream directly to ImageJob and simplify your code significantly.
str.Seek(0, SeekOrigin.Begin);
var Out = new MemoryStream(8096);
new ImageJob(str, Out, new Instructions("width=1110;height="+ Height + ";format=jpg;mode=crop;scale=both")).Build();
Out.Seek(0, SeekOrigin.Begin);
The other sizes you tested each have a Bmp.Height > 300 which means the image requires to be cropped.
However when using an image size 784x250, the image doesn't require to be cropped. So you'll never re-size the image.
Assuming you wish to keep the image proportionally correct, you should first re-size the image to match the wanted width and afterwards crop the exceeding height if necessary.
var image = new Bitmap(str);
var ratio = (double)1110 / image.Width;
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);
var bmp = new Bitmap(newImage);
if(bmp.Height > 300){
//Resize again using crop, like you did in your original code.
}
Related
(How) is it possible to create a qr code using ZXing.Net without a quiet zone?
This is my current code:
BarcodeWriter barcodeWriter = new BarcodeWriter();
barcodeWriter.Format = BarcodeFormat.QR_CODE;
barcodeWriter.Renderer = new BitmapRenderer();
EncodingOptions encodingOptions = new EncodingOptions();
encodingOptions.Width = 500;
encodingOptions.Height = 500;
encodingOptions.Margin = 0;
encodingOptions.Hints.Add(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
barcodeWriter.Options = encodingOptions;
bitmap = barcodeWriter.Write(compressedText);
Thanks!
ZXing.Net doesn't support image scaling with anti-aliasing. That means it can only resize by integer values. In your case you should create the smallest possible image and resize the resulting image with an image manipulation library or the Bitmap and Graphics classes from the framework.
var barcodeWriter = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE
};
// set width and height to 1 to get the smallest possible representation without a quiet zone around the qr code
var encodingOptions = new EncodingOptions
{
Width = 1,
Height = 1,
Margin = 0
};
encodingOptions.Hints.Add(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
barcodeWriter.Options = encodingOptions;
var bitmap = barcodeWriter.Write(compressedText);
// scale the image to the desired size
var scaledBitmap = ScaleImage(bitmap, 500, 500);
private static Bitmap ScaleImage(Bitmap bmp, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / bmp.Width;
var ratioY = (double)maxHeight / bmp.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(bmp.Width * ratio);
var newHeight = (int)(bmp.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(newImage))
{
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(bmp, 0, 0, newWidth, newHeight);
}
return newImage;
}
Update: Clemens's solution is the fastest. I'll leave the alternative I found just in case:
While trying to create a minimal reproducible example as Peter Duniho suggested in the comment, I found that the wrong transparency values were coming from theBitmapImageToBitmapConverter()
It was messing up the whole image. I now load the png straight to a bitmap and scan it and it gives accurate results:
Bitmap bmp = new Bitmap("icon.png");
Console.WriteLine(TransparencyPercentageInImage(bmp));
Question was:
I have a few image controls in a list:
imagesList[index].Source = ReloadIcon(index);
They load images from ".png" files like so:
public BitmapImage ReloadIcon(int index)
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(iconPaths[index], UriKind.Absolute);
image.EndInit();
return image;
}
I then convert those to bitmaps using this converter:
private Bitmap BitmapImageToBitmapConverter(BitmapImage bitmapImage)
{
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
return new Bitmap(bitmap);
}
}
To later scan each pixel for transparency using this code:
private double TransparencyPercentageInImage(Bitmap image)
{
double transpPercentage;
double transpPixelCount = 0;
double totalPixelCount = image.Height * image.Width;
Console.WriteLine("Total pixel count: " + totalPixelCount);
for (int y = 0; y < image.Height; ++y)
{
for (int x = 0; x < image.Width; ++x)
{
if (image.GetPixel(x, y).A == 0) //or !=255
{
transpPixelCount++;
}
}
}
transpPercentage = transpPixelCount / totalPixelCount * 100;
return transpPercentage;
}
Basically, what should I do to get an accurate transparent pixels percentage/count from a bitmap?
I'm looking for the count of absolutely transparent pixels, not semi-transparent.
I'm not really looking for speed here so any solution goes. I'm already using unsafe code, so that's welcome too.
You neither need a System.Drawing.Bitmap nor a WriteableBitmap to access the pixel values in a BitmapSource.
Just call CopyPixels to get the pixel buffer and count the pixels with an alpha value of 0. First make sure you access the buffer in the desired format:
private double GetTransparentPixelsPercentage(BitmapSource bitmap)
{
if (bitmap.Format != PixelFormats.Bgra32)
{
bitmap = new FormatConvertedBitmap(bitmap, PixelFormats.Bgra32, null, 0);
}
var pixelCount = bitmap.PixelWidth * bitmap.PixelHeight;
var pixels = new byte[4 * pixelCount];
bitmap.CopyPixels(pixels, 4 * bitmap.PixelWidth, 0);
var transparentPixelCount = 0;
for (var i = 3; i < 4 * pixelCount; i += 4) // start at first alpha value
{
if (pixels[i] == 0)
{
transparentPixelCount++;
}
}
return (double)transparentPixelCount / pixelCount;
}
Using this method, I want to render a canvas to a bitmap.
When I add a Shape to the Canvas, it is rendered twice the specified size.
In the example below, I am drawing a line from (0;0) to (50;50) on a canvas of size 200 by 200.
public bool exportToBmp(string path, int dpi = 96)
{
if (path == null)
return false;
var canvas = new System.Windows.Controls.Canvas();
// This diagonal Line should span a quarter of the rendered Image
var myLine = new System.Windows.Shapes.Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 0;
myLine.X2 = 50;
myLine.Y1 = 0;
myLine.Y2 = 50;
myLine.StrokeThickness = 2;
canvas.Children.Add(myLine);
canvas.Height = 200;
canvas.Width = 200;
Size size = new Size(canvas.Width, canvas.Height);
canvas.Measure(size);
canvas.Arrange(new Rect(size));
var width = (int)canvas.ActualWidth;
var height = (int)canvas.ActualHeight;
RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, dpi, dpi, PixelFormats.Pbgra32);
bmp.Render(canvas);
PngBitmapEncoder image = new PngBitmapEncoder();
image.Frames.Add(BitmapFrame.Create(bmp));
using (Stream fs = File.Create(path))
{
image.Save(fs);
}
return false;
}
The rendered image I get is 200 by 200 px big, but the diagonal goes all the way to (100;100)
What am I doing wrong?
When I run your code, I see the following image (border added for clarity):
Are you passing a DPI other than 96?
What DPI settings are in use on your computer?
I am loading images onto an excel page, once done this excel is around 48,284 KB This is to large and affects the download speed and is not efficient to what we are trying to achieve.
I am trying to reduce this Excel file size as much as possible and think that reducing the image sizes would do the trick as the excel file without images is around 1000 kb.
This is what i have tried so far with no affect:
public Image ReduceImageSize(Image img)
{
float iwidth = 150;
float iheight = 150;
var brush = new SolidBrush(Color.Black);
var Resizedimage = new Bitmap(img);
float scale = Math.Min(iwidth / Resizedimage.Width, iheight / Resizedimage.Height);
var graph = Graphics.FromImage(Resizedimage);
graph.InterpolationMode = InterpolationMode.Low;
graph.CompositingQuality = CompositingQuality.Default;
graph.SmoothingMode = SmoothingMode.None;
var scaleWidth = (int)(Resizedimage.Width * scale);
var scaleHeight = (int)(Resizedimage.Height * scale);
graph.FillRectangle(brush, new RectangleF(0, 0, iwidth, iheight));
graph.DrawImage(Resizedimage, new Rectangle(((int)iwidth - scaleWidth) / 2, ((int)iheight - scaleHeight) / 2, scaleWidth, scaleHeight));
return Resizedimage;
}
At this point i'm looking for any way to reduce the size if it involves losing some quality or reducing the images dimensions.
Please help.
You made Resizedimage the same size as img.
Use the following code to fix the result image size:
var Resizedimage = new Bitmap(iwidth,iheight);
An example resizing an image keeping the aspect ratio:
Image img = Image.FromFile(#"c:\temp\1.jpg");
Bitmap resizedImg = new Bitmap(150, 150);
double ratioX = (double)resizedImg.Width / (double)img.Width;
double ratioY = (double)resizedImg.Height / (double)img.Height;
double ratio = ratioX < ratioY ? ratioX : ratioY;
int newHeight = Convert.ToInt32(img.Height * ratio);
int newWidth = Convert.ToInt32(img.Width * ratio);
using (Graphics g = Graphics.FromImage(resizedImg))
{
g.DrawImage(img, 0, 0, newWidth, newHeight);
}
resizedImg.Save(#"c:\temp\2.jpg");
I'm using asp.net mvc 3 to stream resized images. But the output of the image is greyish and blury eventhough I've set smoothingmode and interpolationmode to highbiqubic.
public ActionResult ImageTEST(int fileID, int width, int height)
{
var file = _fileRep.GetFile(fileID);
byte[] newFile;
float ratioX = (float)width / (float)file.Width;
float ratioY = (float)height / (float)file.Height;
float ratio = Math.Min(ratioX, ratioY);
int newWidth = (int)(file.Width * ratio);
int newHeight = (int)(file.Height * ratio);
using (var resizedImage = new Bitmap(newWidth, newHeight))
{
using (var source = new Bitmap(new MemoryStream(file.FileContent)))
{
using (var g = Graphics.FromImage(resizedImage))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(source, 0, 0, newWidth, newHeight);
}
}
using (var ms = new MemoryStream())
{
resizedImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
newFile = ms.ToArray();
}
}
return new FileContentResult(newFile, "image/jpeg");
}
Result:
The right one is the exakt same picture but resized in photoshop.
How can I tune this to make quality much better?
First, try to save in a higher quality.
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)100);
foo.Save(filename, ici, ep);
If that is not satisfiable, you may need to use other libraries such as Emgu cv.
The grey problem may because that the color space (AdobeRGB or sRGB) of original image and the one you saved are not the same.