logo

How to Annotate Images from a database in a web page

By Peter Bromberg
Printer Friendly Version
View My Articles
1634 Views
digg facebook google buzz reddit del.icio.us stumble upon twitter
    

Use a specialized Image Control to draw text annotations on an image from a database


In your travels as an ASP.NET developer, you may come across the requirement to be able to display an image - usually a TIFF image of some scanned or faxed document, allow the user to make notes and save them directly into the image, and then persist the annotated image, perhaps while also keeping the original untouched image, back to the database.

In order to do this easily we need to have an image control that will accept a bitmap as its image source, rather than the requirement that the image come from a Url. So I resurrected a control that I used to display images in memory from a database in an older article, and adapted it for this purpose.

The key piece of code for this control is it's Bitmap property:

public Bitmap Bitmap
{
get
{
if (HttpContext.Current.Session["persistenceType"] != null) persistenceType = Persistence.Session;
if (PersistenceType == Persistence.Session)
return (Bitmap) Context.Session[String.Concat(CreateUniqueIDString(), "Bitmap")];
else
return (Bitmap) Context.Cache[String.Concat(CreateUniqueIDString(), "Bitmap")];
}
set
{
if (PersistenceType == Persistence.Session)
Context.Session[String.Concat(CreateUniqueIDString(), "Bitmap")] = value;
else
Context.Cache[String.Concat(CreateUniqueIDString(), "Bitmap")] = value;
}
}

The SQL Script provided with the downloadable solution will create a SQL Server IMAGES table, insert a single TIFF image of a letter, and create stored procedures for performing an insert / update and to select an image by Id.

I abstracted all the business logic for the various operations into a separate class "ImageUtils" as static methods for ease of use:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Web;
namespace Web
{
public class ImageUtils
{
public static Bitmap GetImageFromDb(int id, out int imageId, out string description)
{
SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["imagedb"].ConnectionString);
SqlCommand cmd = new SqlCommand("dbo.GetImages");
cmd.Parameters.AddWithValue("@Id", id);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = cn;
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
DataTable dt = ds.Tables[0];
// if we have an annotated image already in db, use that for display
byte[] imagebytes = (byte[])dt.Rows[0]["OriginalImage"];
if ( dt.Rows[0]["AnnotatedImage"]!=System.DBNull.Value)
//if(imagebytes.Length ==0)
imagebytes = (byte[])dt.Rows[0]["AnnotatedImage"];
HttpContext.Current.Session["originalImage"] = imagebytes;
imageId = Convert.ToInt32(dt.Rows[0]["Id"]);
description = Convert.ToString(dt.Rows[0]["Description"]);
MemoryStream ms = new MemoryStream(imagebytes);
Bitmap bmp = (Bitmap)Image.FromStream(ms);
return bmp;
}
public static bool UpdateImageData(Bitmap originalImage, Bitmap annotatedImage, string description, int id)
{
SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["imagedb"].ConnectionString);
SqlCommand cmd = new SqlCommand("dbo.InsertOrUpdateImage");
byte[] originalImageBytes = (byte[]) HttpContext.Current.Session["originalImage"];
cmd.Parameters.AddWithValue("@OriginalImage",originalImageBytes );
MemoryStream ms = new MemoryStream();
annotatedImage.Save(ms, ImageFormat.Tiff);
cmd.Parameters.AddWithValue("@AnnotatedImage", ms.ToArray());
cmd.Parameters.AddWithValue("@Description", description);
cmd.Parameters.AddWithValue("@Id", id);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = cn;
cn.Open();
int ret = cmd.ExecuteNonQuery();
cmd.Dispose();
cn.Close();
if (ret > 0)
return true;
else
return false;
}
public static Bitmap AnnotateImage(Image myImage,string Message)
{
int len = Message.Length;
int units = len / 15; // use this to scale vertical size of rectangle
Color FillColor = Color.FromArgb(75, 255, 255, 255);
var FillBrush = new SolidBrush(FillColor);
var FillRectangle = new Rectangle(5, 5, 410, 50 * units);
var TextFont = new Font("Arial", 12);
var TextBrush = new SolidBrush(Color.Navy);
var TextFormat = new StringFormat();
TextFormat.Alignment = StringAlignment.Near;
TextFormat.LineAlignment = StringAlignment.Near;
// convert TIFF file to jpg (non-indexed) format so we can "Write on it"
var ms = new MemoryStream();
myImage.Save(ms, ImageFormat.Jpeg);
myImage = Image.FromStream(ms);
Graphics DrawingSurface = Graphics.FromImage(myImage);
DrawingSurface.FillRectangle(FillBrush, FillRectangle);
DrawingSurface.DrawString(Message, TextFont, TextBrush, FillRectangle, TextFormat);
return (Bitmap)myImage;
}
public static int UploadImage(string description,string fileName, byte[] imageBytes)
{
SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["imagedb"].ConnectionString);
SqlCommand cmd = new SqlCommand("dbo.InsertOrUpdateImage");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@OriginalImage", imageBytes);
cmd.Parameters.AddWithValue("@AnnotatedImage", new Byte[0]);
cmd.Parameters.AddWithValue("@Description", description);
cmd.Parameters.AddWithValue("@Id", null);
object o = null;
try
{
cn.Open();
cmd.Connection = cn;
o = cmd.ExecuteScalar();
}
catch
{
throw;
}
finally
{
cmd.Dispose();
cn.Close();
}
return (int) o;
}
}
}

You can see above that the way I size and position the Rectangle to hold the text is pretty simplistic. For a more sophisticated approach, you would probably want to parameterize this process and do measurements. I also provide a utility page that allows you to upload an image from the file system into the database:

string description = this.TextBox1.Text;
string filename = this.FileUpload1.FileName;
byte[] imageBytes = this.FileUpload1.FileBytes;
this.TextBox1.Text = ImageUtils.UploadImage(description, filename, imageBytes).ToString();

The key code in the main display / annotate page codebehind is as follows:

public partial class WebForm1 : Page
{

private string FileName = "LETTER.TIF";
private int imageId = 0;
private string description = String.Empty;

protected void Page_Load(object sender, EventArgs e)
{
// original code (without database)
//ImageControl1.Bitmap = (Bitmap) Image.FromFile(Server.MapPath("images") + "\\" + FileName);
// ImageControl1.PersistenceType = Persistence.Session;
if (!IsPostBack)
{
ImageControl1.Bitmap = ImageUtils.GetImageFromDb(1, out imageId, out description);
this.lblDescription.Text = description;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
//annotate the image and redisplay
Image myImage = ImageControl1.Bitmap;
String Message = TextBox1.Text;
ImageControl1.Bitmap = ImageUtils.AnnotateImage(myImage, Message);
}
#region Web Form Designer generated code
protected override void OnInit(EventArgs e)
{

InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{

}
#endregion

protected void Button2_Click(object sender, EventArgs e)
{
// save image to database
bool retval= ImageUtils.UpdateImageData(null, ImageControl1.Bitmap, this.TextBox1.Text, imageId);
}
}

An important part of the process is that we must convert a TIFF image to a non-indexed format (JPEG) in order to draw on it. When we are done it is saved back to the database in TIFF format again. A Sample annotated image looks like this:





You can download the complete Visual Studio 2008 Solution here.


digg facebook google buzz reddit del.icio.us stumble upon twitter

Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Pete Tweets at peterbromberg No Emails! Post it to the forums, I answer there!



Didn't Find The Answer You Were Looking For?

EggHeadCafe has experts online right now that may know the answer to your question.  We pay them a bonus for answering as many questions as they can.  So, why not help them and yourself by becoming a member (free) and ask them your question right now?
Ask Question In Live Forum

Article Discussion: How to Annotate Images from a database in a web page
Peter Bromberg posted at Sunday, February 14, 2010 10:08 AM
 






Pete's Resume  |  Robbe's Resume  |  Neado  |  Free Icons  |  Privacy  |   (c) 2010