Adding text to an Image programmatically appears blurry - c#

I'm trying to add some text to an Image programmatically and later I save it. But the end result is somewhat not convincing.
The text on the image appears quite blurry.
The snippet I use to render is as follows :
static void ModifyImage()
{
Image origImage;
if (File.Exists(path))
{
using (Stream s = new FileStream(path, FileMode.Open, FileAccess.Read))
{
origImage = Image.FromStream(s);
}
using (Image newImage = new Bitmap(path))
{
// Get the image's original width and height
int originalWidth = origImage.Width;
int originalHeight = origImage.Height;
// To preserve the aspect ratio
float ratio = Math.Min((float)originalWidth, (float)originalHeight);
Graphics graphics = Graphics.FromImage(newImage);
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
string strSoftwareVersion = "Version " + StrVersion;
var oVersionFont = new Font("Arial", 15f, FontStyle.Bold);
SizeF strSize = graphics.MeasureString(strSoftwareVersion, oVersionFont);
float mX = (float)((originalWidth * 0.85) - 5.0);
float mY = (float)((originalHeight * 0.85) - 5.0);
float mWidth = (float) (strSize.Width + 5.0);
float mHeight = (float)(strSize.Height + 10.0);
/*
* Alternate I tried.
*/
//GraphicsPath blackfont = new GraphicsPath();
//SolidBrush brsh = new SolidBrush(Color.White);
//blackfont.AddString("TEST APP", oVersionFont.FontFamily, (int)FontStyle.Bold, 15, new Point((int)(originalWidth * 0.85), (int)(originalHeight * 0.85)), StringFormat.GenericDefault);
//graphics.FillPath(brsh, blackfont);
/*
*Software Version String
*/
graphics.DrawString("Version " + StrVersion, new Font("Arial", 15f, FontStyle.Bold), Brushes.White, (int)(originalWidth * 0.85), (int)(originalHeight * 0.85));
/*
* Save processed image
*/
newImage.Save(newPath, ImageFormat.Jpeg);
}
origImage.Dispose();
}
}
As you can see from the image, the blurriness of the text and surrounding areas are quite prominent. I'm quite sure this is due to the Aliasing or TextRendering issue with the alpha levels, but just not able to point the exact issue.

The text looks fine to me.
To make it crispier you can turn off all antialiasing.
The 'surrounding areas' obviously show some jpeg artifacts.
Good text and jpeg don't go together well.
Either turn up jpeg quality settings up from the default (of around 75%) or go for png!
Note that 90-95% quality, while greatly improving text quality also greatly enlarges size and going for png may well save you space in addition to being lossless..

I had a similar issue with the text being quite badly aliased when drawn onto the image. This is my solution which produces a reasonable text quality.
"text" is a System.Windows.Media.FormattedText instance.
BitmapFrame originalImageSource = BitmapFrame.Create(new Uri(file.FullName), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
var visual = new DrawingVisual();
using(DrawingContext drawingContext = visual.RenderOpen()) {
drawingContext.DrawImage(originalImageSource, new Rect(0, 0, originalImageSource.PixelWidth, originalImageSource.PixelHeight));
drawingContext.DrawText(text, topRight);
}
var renderTargetBitmap = new RenderTargetBitmap(originalImageSource.PixelWidth,
originalImageSource.PixelHeight,
originalImageSource.DpiX, originalImageSource.DpiY,
PixelFormats.Pbgra32);
renderTargetBitmap.Render(visual);
BitmapFrame bitmapFrame = BitmapFrame.Create(renderTargetBitmap);
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bitmapFrame);
encoder.Save(stream);

Related

Blurry and large image barcode being generated using a plugin in Windows Form

Going through the steps mentioned here
and using IDAutomationCode39, I am getting the barcode image, however they are very blurr and only scans bigger size images. My barcode id will be upto 30 characters long, which is causing a very wide barcode image. Where could the problem lie? Is it the IDAutomationCode39 or my setting in my button click event below?
private void button1_Click(object sender, EventArgs e)
{
string abbre = GenerateProdCodeFromProductName();
txt2.Text = abbre;
string barcode = txt1.Text;
Bitmap bitm = new Bitmap(barcode.Length * 45, 160);
bitm.SetResolution(240, 240);
using (Graphics graphic = Graphics.FromImage(bitm))
{
Font newfont = new Font("IDAutomationHC39M", 6);
PointF point = new PointF(5f, 5f);
SolidBrush black = new SolidBrush(Color.Black);
SolidBrush white = new SolidBrush(Color.White);
graphic.FillRectangle(white, 0, 0, bitm.Width, bitm.Height);
graphic.DrawString("*" + barcode + "*", newfont, black, point);
}
using (MemoryStream Mmst = new MemoryStream())
{
bitm.Save("ms", ImageFormat.Jpeg);
pictureBox1.Image = bitm;
pictureBox1.Width = bitm.Width;
pictureBox1.Height = bitm.Height;
}
}
Thank you.
I don't see any PlugIn reference in your code, you are just using a Code39 ASCII font to print a Barcode on a Bitmap.
The problem I see is that the size of the resulting Bitmap is unscaled.
What I mean is, you let the size of the Barcode determine the size of the final graphic image.
It is usually the opposite. You have some defined dimensions for a Bitmap, because of layout constraints: you have to print the barcode to a label, for example.
If the generated Barcode is wider than its container, you have to normalize it (scale it to fit).
Here is what I propose. The size of the Bitmap if fixed (300, 150). When the Barcode is generated, its size is scaled to fit the Bitmap size.
The quality of the image is preserved, using an Bicubic Interpolation in case of down scaling. The resulting graphics is also Anti-Aliased.
(Anti-Alias has a good visual render. May not be the best choice for printing. It also depends on the printer.)
The generated Bitmap is then passed to a PictureBox for visualization and saved to disk as a PNG image (this format because of its loss-less compression).
Here is the result:
string Barcode = "*8457QK3P9*";
using (Bitmap bitmap = new Bitmap(300, 150))
{
bitmap.SetResolution(240, 240);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
Font font = new Font("IDAutomationSHC39M", 10, FontStyle.Regular, GraphicsUnit.Point);
graphics.Clear(Color.White);
StringFormat stringformat = new StringFormat(StringFormatFlags.NoWrap);
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
graphics.TextContrast = 10;
PointF TextPosition = new PointF(10F, 10F);
SizeF TextSize = graphics.MeasureString(Barcode, font, TextPosition, stringformat);
if (TextSize.Width > bitmap.Width)
{
float ScaleFactor = (bitmap.Width - (TextPosition.X / 2)) / TextSize.Width;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.ScaleTransform(ScaleFactor, ScaleFactor);
}
graphics.DrawString(Barcode, font, new SolidBrush(Color.Black), TextPosition, StringFormat.GenericTypographic);
bitmap.Save(#"[SomePath]\[SomeName].png", ImageFormat.Png);
this.pictureBox1.Image = (Bitmap)bitmap.Clone();
font.Dispose();
}
}

Print High Resolution Image in C#

I am printing image 2349 x 3600 pixels. I have resized image but printing is blurred not clean. Please looke at code -
using System.Drawing.Drawing2D;
public Bitmap resizeimage(Bitmap bitmap)
{
Bitmap result = new Bitmap(850, 1101);
using (Graphics grap = Graphics.FromImage(result))
{
grap.CompositingQuality = CompositingQuality.HighQuality;
grap.InterpolationMode = InterpolationMode.Bicubic;
grap.SmoothingMode = SmoothingMode.HighQuality;
grap.CompositingQuality = CompositingQuality.HighQuality;
grap.DrawImage(bitmap, 0, 0, 850, 1101);
}
return result;
}
I tried everything from changing bitmap size, quality of graphics but still image blurred.
I used microsoft office 2007 and resized image and printed it , it was so clear.
How I can get exact printing quality as I got in microsoft office 2007.
Please help.
Here is code before drawing -
PrintPreviewDialog printpreview = new PrintPreviewDialog();
PrintDocument printdocument = new PrintDocument();
printdocument.PrinterSettings.PrinterName = "EPSON L100 Series";
int horizantal_dpi = printdocument.PrinterSettings.DefaultPageSettings.PrinterResolution.X;
int vertical_dpi = printdocument.PrinterSettings.DefaultPageSettings.PrinterResolution.Y;
decimal final_width_dpi = (((int)printdocument.DefaultPageSettings.PrintableArea.Width * horizantal_dpi) / 100);
decimal final_height_dpi = (((int)printdocument.DefaultPageSettings.PrintableArea.Height * vertical_dpi ) / 100);
printimagaprint = new Bitmap((int)final_width_dpi, (int)final_height_dpi);
//set resoultion
printimagaprint.SetResolution(horizantal_dpi, vertical_dpi);
Graphics g = System.Drawing.Graphics.FromImage(printimagaprint);
g.DrawImage(bitmap, 0, 0, printimagaprint.Width, printimagaprint.Height);
printdocument.PrintPage +=new PrintPageEventHandler(printdocument_PrintPage);
//printdocument.Print();
printdocument.DocumentName = textBox1.Text;
printpreview.Document = printdocument;
printpreview.ShowDialog();
Try matching the printer resolution before printing.
printDialog.PrinterSettings.PrinterName = GetTargetPrinter();
int horizontal_dpi = printDialog.PrinterSettings.DefaultPageSettings.PrinterResolution.X;
int vertical_dpi = printDialog.PrinterSettings.DefaultPageSettings.PrinterResolution.Y;
Decimal final_width_dpi = (((int)printDialog.PrinterSettings.DefaultPageSettings.PrintableArea.Width * horizontal_dpi) / 100);
Decimal final_height_dpi = (((int)printDialog.PrinterSettings.DefaultPageSettings.PrintableArea.Height * vertical_dpi) / 100);
printImage = new Bitmap((int)final_width_dpi, (int)final_height_dpi);
// Set Resolution
printImage.SetResolution(horizontal_dpi, vertical_dpi);
Graphics g = System.Drawing.Graphics.FromImage(printImage);
And please try to provide more descriptive code. I am just making assumption for now.

Adding copyright to an image in C# - strange behaviour

I'm trying to add copyright to an image. If the resolution of image is less than 4592x2576, it works as I want. But in second case (in this case if the resolution is equals 4592x2576) it adds copyright too big size.
Look attachments for details.
class Program
{
private static string file5 = "d:\\DSC01305.JPG";
private static string file6 = "d:\\DSC01427.JPG";
static void Main(string[] args)
{
AddCopyrightWithText(file5);//good
AddCopyrightWithText(file6);//not good
}
private const string CopyrightText = "mysite.com";
private const int MaxFontSize = 190;
const int coefficient = 20;
public static void AddCopyrightWithText(string fileName)
{
using (var img = Image.FromFile(fileName))
{
using (var gr = Graphics.FromImage(img))
{
var color = Color.FromArgb(90, 241, 235, 105);
int fontSize = img.Width / coefficient;
if (fontSize > MaxFontSize)
fontSize = MaxFontSize;
var font = new Font("Comic Sans MS", (float)fontSize, FontStyle.Bold);
var stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
gr.SmoothingMode = SmoothingMode.AntiAlias;
int y = img.Height - (int)gr.MeasureString(CopyrightText, font, int.MaxValue).Height;
int x = img.Width / 2;
gr.DrawString(CopyrightText, font, new SolidBrush(color), new Point(x, y), stringFormat);
}
using (var ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Jpeg);
img.Dispose();
File.Delete(fileName);
var file = new FileStream(fileName, FileMode.Create, FileAccess.Write);
ms.Seek(0, SeekOrigin.Begin);
ms.WriteTo(file);
file.Close();
file.Dispose();
}
}
}
}
In the second case if I set fontSize = 182 manually (becase in the first case fontSize is equals 182), there is no effect, the result is the same!
How do I fix it?
P.S. First and second attachments show the result I want, third and fourth shows the bug. Pay attention in third attachment I changed the fontSize manually from 190 to 182.
Original image below
Original image below
Original image below
Original image below
It's necessary to use GraphicsUnit.Pixel
var font = new Font("Comic Sans MS", (float)fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
The reason is, the constructor for Font that doesn't take a GraphicsUnit parameter, will use GraphicsUnit.Point.
This relates to the DPI information which for some image formats is stored in the image (for others it uses defaults from the system). Points are relative to inches, so different DPIs for the image means different sizes in pixels. Therefore different images end up having the text in different sizes. This would be wonderful if we wanted to print the images according to their DPI information and have the same size text in each printout. For on-screen use to be consistent, it's pixel-sizes that are important, so we make that explicit.

.NET - Border around resized imaged

I'm trying to resize an image in .NET, but get a faint black border around the resized image. I found a post - http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/cf765094-c8c1-4991-a1f3-cecdbd07ee15/ which from someone who said making the destination rectangle larger than the canvas worked, but this doesn't work for me. It gets riid of the top and left borders, but the right and bottom are still there, and are a full 1px thick black.
Am I missing something? My code is below.
Image image = ... // this is a valid image loaded from the source
Rectangle srcRectangle = new Rectangle(0,0,width, height);
Size croppedFullSize = new Size(width+3,height+3);
Rectangle destRect = new Rectangle(new Point(-1,-1), croppedFullSize);
using(Bitmap newImage = new Bitmap(croppedFullSize.Width, croppedFullSize.Height, format))
using(Graphics Canvas = Graphics.FromImage(newImage)) {
Canvas.SmoothingMode = SmoothingMode.AntiAlias;
Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
Canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
Canvas.FillRectangle(Brushes.Transparent, destRect);
Canvas.DrawImage(image, destRect, srcRectangle, GraphicsUnit.Pixel);
newImage.Save(filename, image.RawFormat);
}
Simply provide the DrawImage method with an ImageAttributes instance which has WrapMode set to TileFlipXY. This will prevent the edge from blending against the background color.
For sample code that doesn't leak memory like the other answers here, see this gist
Try it like this, i think i've never got a black border...
If you want to use System.Drawing libraries:
using (var sourceBmp = new Bitmap(sourcePath))
{
decimal aspect = (decimal)sourceBmp.Width / (decimal)sourceBmp.Height;
int newHeight = (int)(newWidth / aspect);
using (var destinationBmp = new Bitmap(newWidth, newHeight))
{
using (var destinationGfx = Graphics.FromImage(destinationBmp))
{
destinationGfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
destinationGfx.DrawImage(sourceBmp, new Rectangle(0, 0, destinationBmp.Width, destinationBmp.Height));
destinationBmp.Save(destinationPath, ImageFormat.Jpeg);
}
}
}
or you can do the same with wpf, like this:
using (var output = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None))
{
var imageDecoder = BitmapDecoder.Create(inputStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
var imageFrame = imageDecoder.Frames[0];
decimal aspect = (decimal)imageFrame.Width / (decimal)imageFrame.Height;
var height = (int)(newWidth / aspect);
var imageResized = new TransformedBitmap(imageFrame,new ScaleTransform(
newWidth / imageFrame.Width * Dpi / imageFrame.DpiX,
height / imageFrame.Height * Dpi / imageFrame.DpiY, 0, 0));
var targetFrame = BitmapFrame.Create(imageResized);
var targetEncoder = new JpegBitmapEncoder();
targetEncoder.Frames.Add(targetFrame);
targetEncoder.QualityLevel = 80;
targetEncoder.Save(output);
}
I recommend the WPF way. The compression & quality seems better...
For me it was a bad Bitmap parameter. Instead of this:
new Bitmap(width, height, PixelFormat.Format32bppPArgb);
Just remove the PixelFormat to this:
new Bitmap(width, height);
And everything was ok then.
With the PixelFormat I had black border on the top and left border. Then I tried g.PixelOffsetMode = PixelOffsetMode.HighQuality; which seemed fine at first. But then I noticed light gray borders around the whole image.

Image resizing with GDI+

I'm really trying to nail out a little more performance out of this tidbit of code. It's not a heavly used bit of code but is used every time a new image is uploaded, and 4 times for each image (100px, 200px, 500px, 700px). So when there are any more than 2 or 3 images processing, it gets a little busy on the server. Also I'm trying to figure out how to make it correctly process images with a low resolution. Currently it just chops it off half way through, not plesent.
Examples: Original, large, xLarge
public static byte[] ResizeImageFile(byte[] imageFile, int targetSize)
{
using (System.Drawing.Image oldImage = System.Drawing.Image.FromStream(new MemoryStream(imageFile)))
{
Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format32bppRgb))
{
newImage.SetResolution(oldImage.HorizontalResolution, oldImage.VerticalResolution);
using (Graphics canvas = Graphics.FromImage(newImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
}
private static Size CalculateDimensions(Size oldSize, int targetSize)
{
Size newSize = new Size();
if (oldSize.Width > oldSize.Height)
{
newSize.Width = targetSize;
newSize.Height = (int)(oldSize.Height * (float)targetSize / (float)oldSize.Width);
}
else
{
newSize.Width = (int)(oldSize.Width * (float)targetSize / (float)oldSize.Height);
newSize.Height = targetSize;
}
return newSize;
}
Thanks for and help!
The first thought that comes to mind is, have you thought about Multithreading it? i.e. calling this method for each image (or batch of images) in a separate thread? That way, if your server has a few cores you can get things done quicker. Just a thought...
(Threading is a great tip.)
Try to call your method with the smallest possible image as input each time, instead of the original image. If the original image is, say 2000px, then create the 700px image from it and then use your newly created 700px image to create the 500px, etc...
With the HighQualityBicubic setting I doubt that you'll notice any difference in the 100px image. (But it of course it needs to be verified.)
For completeness, here is the solution to the second part of the question which was never answered. When processing a low resolution image the image was being cut off. The solution now, seems obvious. The problem lies in this bit of code from above:
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height,
PixelFormat.Format32bppRgb))
The problem being that I'm selecting the PixelFormat, not letting it be the format of the original image. The correct code is here:
public static byte[] ResizeImageFile(byte[] imageFile, int targetSize)
{
using (System.Drawing.Image oldImage = System.Drawing.Image.FromStream(new MemoryStream(imageFile)))
{
Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height,
oldImage.PixelFormat))
{
newImage.SetResolution(oldImage.HorizontalResolution,
oldImage.VerticalResolution);
using (Graphics canvas = Graphics.FromImage(newImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
}

Categories