Image Composition? (montage builder code)

Discussion in 'ASP.NET / ASP.NET Core' started by gatumbiz000, Apr 27, 2009.

Thread Status:
Threads that have been inactive for 5 years or longer are closed to further replies. Please start a new thread.
  1. I've been on this for days!!! I want to:

    1. read several image JPGs from urls
    2. crop them
    2. combine them in a montage (as one image)

    It IS possible - I've already got a hosted Unix PHP program which does this. It's just one (sic) command using ImageMagick. No probs... it worked out of the box.

    So far, I'm at a dead end with ASP.NET

    1. I've looked at GDI+ (System.Drawing). It seems to only support single image manipulation (crop, resize, shade, etc.). I can't see anything related to "compose", "montage", "tile", "mosaic", ie. anything which will allow me to combine the images.

    2. I tried System.Windows.Media.Imaging, but the DASP WindowsCodecs.dll, required for JPG conversion. was incompatible with my VS2008 output. Anyway, I'm doubtful that media.imaging will provide what I want.

    3. I tried to use ImageMagick. It's not supported by DASP, so i tried to install it in my directory, and run it as from the command line. This involed recompiling it from source (to get a standalone version). Still, after a day, I couldn't get it working.

    Help! How do I do this???

    Thanks,

    Stephen
     
  2. GDI+ is the way to go so you were there with your first choice. You need an iteration of System.Drawing.Graphics.DrawImage method calls to achieve this.

    An outline of how to go is:

    1) Load each of your images to a System.Drawing.Graphics to perform the cropping operation. Here is some rough c# sample code to get you started - the CropImageFile method returns byte array but you can change this to return a System.Drawing.Image if you need to:

    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Imaging;
    using System.IO;

    namespace Imaging
    {
    public class Imaging
    {
    /// <summary>
    /// Crops the image file.
    /// </summary>
    /// <param name="imageFile">The image file.</param>
    /// <param name="targetW">The target W.</param>
    /// <param name="targetH">The target H.</param>
    /// <param name="targetX">The target X.</param>
    /// <param name="targetY">The target Y.</param>
    /// <returns></returns>
    public static byte[] CropImageFile(byte[] imageFile, int targetW, int targetH, int targetX, int targetY)
    {
    //a quick and dirty image crop method
    //this code can be improved with using statements to ensure resources are correctly disposed.
    Image img = Image.FromStream(new MemoryStream(imageFile));
    Bitmap bmp = new Bitmap(targetW, targetH, PixelFormat.Format24bppRgb);
    //whatever resolution you need
    bmp.SetResolution(72, 72);

    Graphics gr = Graphics.FromImage(bmp);
    //whatever graphics options you need
    gr.SmoothingMode = SmoothingMode.AntiAlias;
    gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
    gr.PixelOffsetMode = PixelOffsetMode.HighQuality;

    //perform the crop - you can move the origin if you need to
    gr.DrawImage(img, new Rectangle(0, 0, targetW, targetH), targetX, targetY, targetW, targetH, GraphicsUnit.Pixel);

    // Save the result back out - dispose of all objects to make sure the files don't stay locked.
    MemoryStream mm = new MemoryStream();
    bmp.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
    img.Dispose();
    bmp.Dispose();
    gr.Dispose();

    return mm.GetBuffer();
    }
    }
    }


    2) Create a System.Drawing.Graphics instance large enough to hold all of the target images you want to draw on there. You will probably need each of your images in a System.Drawing.Image instance to determine the image sizes using the Height and Width props (or Size) so you can create your target Graphics instance at the right size.

    3) For each of your images, iteratively use one of the overloaded System.Drawing.Graphics.DrawImage methods to draw each image on your target Graphics surface at the right position. This is the stitching process.

    ..there shouldn't be a lot of code for all of this and the trickiest part will be the calculation of the image positioning layout on the target graphics instance..but even that shouldn't be too hard. Granted it will be more than the one line of code you had when you used ImageMagick, however ImageMagick was obviously doing some internal "magic" for you that would have involved more than a single line of code..and with .NET GDI+ you will have full control of the cropping / stitching operation in your own code.

    Good luck,
    Cheers,
    Joe
     
  3. Thanks Joe. System.Drawing.Graphics worked just as you described, and I created my montage by drawing to different parts of the canvas. Where I went wrong before was that I was looking in System.Drawing.Image. I'm completely new to image processing in Windows.

    Regards,

    Stephen
     
  4. Hi Stephen, I'm happy to hear you worked out a solution
    All the best,
    Joe
     
  5. As I said in my first post, I couldn't find a solution anywhere to the problem of building a montage/mosaic with GDI+.

    It's not hard, if you understand GDI++, but, if you are completely new to it, then it can leave you stumped.

    So, here's a simple montage builder. And yes, it isn't very simple! :))

    It reads images from the current directory, and builds a 2 x 2 montage.

    It has fixed dimensions, and no margins around the tiles. A real world example will probably paramterise these things, but that's just a bit of extra maths on top of this code.

    // We'll use an image array for input.
    // Get files in the current directory, and read them in as images.
    var files = System.IO.Directory.GetFiles(".", "*.jpg");
    Image[] images = new Image[files.Length];
    int i = 0;
    foreach (string file in files) {
    images = Image.FromFile(file);
    i++;
    }

    Bitmap bm;
    Graphics canvas;

    // Set dimensions.
    // This is where you'll paramaterize it with canvas size, margins, and number of tiles.
    var canvasWidth = 200;
    var rowWidth = 2; // Number of tiles across, and down.
    var tileWidth = canvasWidth / rowWidth;

    // Build the canvas
    bm = new Bitmap(canvasWidth, canvasWidth);
    canvas = Graphics.FromImage(bm);

    // Fill the canvas with a background color
    // For our simple example, this background won't be seen, as the tiles fill up the
    // whole canvas.
    Region canvasRegion = new Region(new Rectangle(0, 0, bm.Width, bm.Height));
    Brush fillBrush = new SolidBrush(System.Drawing.Color.GhostWhite);
    canvas.FillRegion(fillBrush, canvasRegion);

    // Draw the images on the canvas
    var count = 0;
    int x_tile, y_tile = 0; // moving pointers to where we'll place the images on the bitmap
    while (count < images.Count())
    {
    x_tile = (count % rowWidth) * tileWidth;
    y_tile = (count / rowWidth) * tileWidth;
    Point[] region = {new Point(x_tile,y_tile),
    new Point(x_tile+tileWidth,y_tile),
    new Point(x_tile,y_tile + tileWidth)};
    // You'll probably need to crop your image here. We'll use the raw image.
    // var cropped_im = SomeCropFunction(imageages[count], crop dimensions.. );
    var cropped_im = images[count];
    try
    {
    canvas.DrawImage(cropped_im, region);
    }
    catch (System.ArgumentException) { }
    count++;
    }

    foreach (System.Drawing.Image image in images)
    {
    image.Dispose();
    }

    // All done! Save the image.
    bm.Save("montage.jpg");
     
Thread Status:
Threads that have been inactive for 5 years or longer are closed to further replies. Please start a new thread.

Share This Page