Archive

Posts Tagged ‘image’

Resize images for mobile web

January 13, 2009 2 comments

If you decide that your mobile website won’t be nothing more than a translation of your website, then you have to look into resizing the images too. There are pretty good reasons for doing it:

  • mobile devices supports images only up to a certain file size
  • mobile devices supports images only up to a certain image width and height
  • mobile devices supports web pages only up to a certain total size (page + resources)
  • performance, when it comes to bandwidth
  • user experience, when it comes to rendering capabilities

Next, I will present a short and practical solution, implemented as an aspx page. The page is taking only one parameter (i), which is the URL of the original image, downloads it, resizes it and sends it back to the client. This can also be implemented as an HTTP filter as well: every time an image is requested, it gets the original image, resizes it and pushes it into the response. The advantage as a page is that you can process this way, even images from other servers.
As for how to recognize the user mobile device, we will use my favorite, WURFL. For more information see Mobile device recognition.

I will include here only the C# code with enough comments so I won’t need to say anything else. 😀
But before, one more thing: what is the new size of the image? First of all we need the maximum image width and height supported by the user mobile device. Here, WURFL is the solution. Then I made the supposition that the images are optimized for 800×600 and I will reduce the image proportionally to the maximum width and height supported by the mobile device. To not give anomalies, I also introduce a minimum width and height, not to go below.

public partial class MobileImageResizer : System.Web.UI.Page
    {
        // image should not be reduce under this width
        private static int MIN_WIDTH = 24;
        // image should not be reduce under this height
        private static int MIN_HEIGHT = 24;
        // images on the desktop version are optimized for this width
        private static int OPTIMIZED_WIDTH = 800;
        // images on the desktop version are optimized for this height
        private static int OPTIMIZED_HEIGHT = 600;

        protected void Page_Load(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            // get the image URL
            String url = context.Request.QueryString.Get("u");

            // if no URL is specified in the u parameter then send a NOT_FOUND error
            if (url == null)
            {
                Utils.Send(context.Response, HttpStatusCode.NotFound);
                return;
            }
            
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            // specify a timeotu for the request
            // request.Timeout = 10000; // 10s
            // if you want use a proxy for request
            // request.Proxy = WebProxy;
            HttpWebResponse initialResponse = (HttpWebResponse)request.GetResponse();
                    
            // if something went wrong don't try to transform
            if (initialResponse.StatusCode != HttpStatusCode.OK)
                throw new HttpException((int)initialResponse.StatusCode, initialResponse.StatusDescription);

            context.Response.ContentType = initialResponse.ContentType;
            Image image = Bitmap.FromStream(initialResponse.GetResponseStream());

            // get the maximum image size from the WURFL database
            // see my other article about <a href="https://beradrian.wordpress.com/2008/09/04/mobile-device-filter-in-aspnet/">WURFL filter</a>
            int maxWidth = ...;
            int maxHeight = ...;

            // calculate the resize ratio
            double ratio = Math.Min( // final ratio
                    Math.Max( // final width ratio
                        ((double)maxWidth) / Math.Max(image.Width, OPTIMIZED_WIDTH), // width ratio
                        ((double)MIN_WIDTH) / image.Width // minimum width ratio
                    ),
                    Math.Max( // final height ratio
                        ((double)maxHeight) / Math.Max(image.Height, OPTIMIZED_HEIGHT), // height ratio
                        ((double)MIN_HEIGHT) / image.Height // minimum height ratio
                    )
                    );

            if (ratio &lt; 1)
            {
                // if we have to reduce the image
                int newWidth = (int)(image.Width * ratio);
                int newHeight = (int) (image.Height * ratio);

                Image newImage = new Bitmap(newWidth, newHeight, image.PixelFormat);
                Graphics g = Graphics.FromImage(newImage);
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                Rectangle r = new Rectangle(0, 0, newWidth, newHeight);
                g.DrawImage(image, r);
                newImage.Save(context.Response.OutputStream, image.RawFormat);
            }
            else 
            {
                // otherwise simply send the image
                // we don't make bigger the smaller images
                image.Save(context.Response.OutputStream, image.RawFormat);
            }

            // finish
            context.Response.End();
        }
    }
Categories: Software, Web Tags: , , ,

Rotate images according to EXIF info

November 14, 2008 6 comments

Some digital cameras are saving the snapshots as they come from the digital sensor. This is not always the right orientation, as it depends on how you are position the camera. Luckily, the cameras are also saving the orientation information into EXIF attributes.
This article is about how to automatically correct this from your Java program, using jMagick. The entire thing can be break down into pieces: read an EXIF attribute for an image, interpret the value and transform the image.
The EXIF attribute responsible for orientation is called Orientation. According to EXIF specs, the values have the following meaning:

  • 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
  • 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
  • 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
  • 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
  • 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
  • 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
  • 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
  • 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
  • Other = reserved

Graphically, this looks like this: orientation.
The following transformations should be applied to the images according to their EXIF orientation:

Autorotate EXIF images

Autorotate EXIF images


where the possible operations are horizontal or vertical flip and rotate (90 degrees clockwise or counter-clockwise and 180 degrees). The corresponding jMagick methods from MagickImage class are:

  • horizontal flip – flip()
  • vertical flip – flop()
  • rotate 180 degrees – rotate(180)
  • rotate 90 degrees clockwise – rotate(90)
  • rotate 90 degrees counter-clockwise – rotate(-90)

Now let’s translate everything into Java code:

private static final int NONE = 0;
private static final int HORIZONTAL = 1;
private static final int VERTICAL = 2;
private static final int[][] OPERATIONS = new int[][] {
        new int[] {  0, NONE},
        new int[] {  0, HORIZONTAL},
        new int[] {180, NONE},
        new int[] {  0, VERTICAL},
        new int[] { 90, HORIZONTAL},
        new int[] { 90, NONE},
        new int[] {-90, HORIZONTAL},
        new int[] {-90, NONE},
        };

public static MagickImage rotateByExif(MagickImage image) throws MagickException {
    try {
        int index = Integer.parseInt(image.getImageAttribute("EXIF:Orientation")) - 1;
        int degrees = OPERATIONS[index][0];
        if (degrees != 0)
            image = image.rotateImage(degrees);
        switch (OPERATIONS[index][1]) {
            case HORIZONTAL:
                image = image.flopImage();
                break;
            case VERTICAL:
                image = image.flipImage();
        }
    }
    catch (NumberFormatException exc) {}
    catch (NullPointerException exc) {}
    return image;
}

Now you can simply call the rotateByExif method. For more information on how you can use jMagick to load and save images please see the official documentation or see Watermark with jMagick article. Actually you can even integrate this, so that you apply it to the watermarked images.

So actually it is pretty simple to rotate images according to the EXIF info using jMagick: just get the EXIF Orientation attribute with getImageAttribute() method and, according to its value, apply the rotateImage, flipImage and flopImage.

Categories: Web Tags: ,

Watermark with JMagick

October 13, 2008 2 comments

I know you can do watermarking in thousands of ways, but this is if you want to embed it in your Java application. I know that you can also do this with Java2D, but it’s so much easier with ImageMagick. Moreover you have JMagick distributions for most of the major OSes.
First of all, if you haven’t heard about JMagick, this is the Java/JNI implementation for ImageMagick.
Unfortunately the JMagick documentation is very few and it includes only a few examples.
This next example will guide step by step in adding a watermark to an image. The idea is simple: first you create a watermark image from a text and then combine this image with your initial image.
So, let’s create the watermark image. If you want to watermark more than one image, this will be a one time only step.

    // create the watermark image info structure
    ImageInfo markImageInfo = new ImageInfo();
    // set a size for the image high enough to accommodate the text 
    markImageInfo.setSize("1024x1024");
    // create the image
    markImage = new MagickImage();
    markImage.allocateImage(markImageInfo);
    // make the image transparent
    markImage.setMatte(true);
    // set the background image to black
    // actually the color doesn't matter, only the alpha value - 65535
    markImage.setBackgroundColor(new PixelPacket(0, 0, 0, 65535));
    // make the initial black background color transparent
    markImage.transparentImage(new PixelPacket(0, 0, 0, 0), 65535);

Now what we have is an image with a black transparent background. Next we have to paint the text on it. For this we will create the drawing and put it into the image.

    // create the drawing info structure
    ImageInfo drawInfo = new ImageInfo();
    // create the drawing structure
    DrawInfo draw = new DrawInfo(drawInfo);
    draw.setOpacity(0);
    draw.setGeometry("+0+0");
    draw.setGravity(GravityType.CenterGravity);    
    // set the watermark color to gray
    draw.setFill(new PixelPacket(0xaf00, 0xaf00, 0xaf00, 0));
    // set the font size
    draw.setPointsize(48);
    // set the font name or the path to the font file
    draw.setFont("Arial");
    // set the watermark text
    draw.setText("Adrian's blog");
    // make the text smoother
    draw.setTextAntialias(true);

Now we have the text and we have to put it into the watermark image:

    // draw the text on the image
    markImage.annotateImage(draw);
    // remove the transparent borders of the image so that only the text will remain
    markImage = markImage.trimImage();

The initial setup is finished. Now we have to load every image that we want to watermark it, combine it with the watermark image and save it.

    // load the image from the specified filename
    ImageInfo imageInfo = new ImageInfo(filename);
    MagickImage image = new MagickImage(imageInfo);
    // watermark the image
    image.compositeImage(CompositeOperator.HardLightCompositeOp, markImage, 10, 10);
    // save the image
    image.setFileName(watermarkedFilename);
    image.writeImage(imageInfo);

The watermark is put in the upper left corner, 10 pixels away from both borders. This is actually what the third and fourth parameters of the compositeImage method represent.
Instead of CompositeOperator.HardLightCompositeOp you may also use CompositeOperator.SoftLightCompositeOp or CompositeOperator.OverlayCompositeOp.
If you probably watermark the image you may also want to resize it

    image = image.scaleImage(newWidth, newHeight);

or remove the EXIF and IPTC info from it

    image.profileImage("*", null);

So, in just a few easy steps you were able to watermark an image (or more) with your text. The nice part is that using JMagick you can do a lot of other stuff with the same ease.
This solution can be easily embedded into GUI or web applications.

Later edit: I also tried the DissolveOp and the results are very good.

Categories: Software Tags: ,

Automatically processed images

May 27, 2008 Leave a comment

As I’m not a graphic designer, I’m not interested very much in image processing. In this area my experience is pretty thin and reduced of simply retouching my own photos or small adapting of some icons and graphics for my applications.
Although, one part of the image processing raises more interest to me: automatic processing. Why would you need such thing? Maybe not clear in the beginning, but this could have huge impact on your projects, like automatic generation of image galleries, adding logos or watermark to pictures and you already got the idea and how useful could be.
Before going any further I have to make one small clarification. As I have mostly a Java background, I use Ant, which is the equivalent of make from the Linux world. And of course, I’ll be interested of integrating the automatic image processing with Ant.
Probably now, the Ant advanced users will jump right on and say: “That’s easy! Use the Image Ant task“. Not quite. This could be one solution, but it is far from easy and complete. Let’s start with the installation. First of all, you should download and install JAI libraries. Second you have to declare(define) the image task and then use it. And that’s the good part. The worst part is that you don’t have support for most of the common operations with images.
So I needed another solution. Something easy to integrate in Ant, cross-platform (at least for the sake of Java principle) and supporting at least the common operations with images. And I came across ImageMagick, a very well-known image processing program with command line interface.
As you all Ant users know, this is very easy to integrate using the exec task and it features huge amounts of image processing operations, enabling you to do almost anything you can think of.
Now for the sake of example, I’ll paste here some parts of build.xml and what you can do with it.

  • Change one color with another in an image (great for automatically creating skins)

    <exec executable="${imagemagick.dir}/convert">
        <arg value="skinnable.gif"/>
    
        <arg value="-fill"/>
        <arg value="blue"/>
        <arg value="-opaque"/>
        <arg value="#ffffff"/>
    
        <arg value="blue_skin.gif"/>
    </exec>

  • Generate images from text (great for automatically creating buttons)

    <exec executable="${imagemagick.dir}/convert">
        <!-- background color -->
        <arg value="-background"/>
        <arg value="#ffffff"/>
        <!-- color of the text margin -->
        <arg value="-stroke"/>
        <arg value="#202020"/>
        <arg value="-strokewidth"/>
        <arg value="1"/>
        <!-- color of the text -->
        <arg value="-fill"/>
        <arg value="#909090"/>
        <!-- path to font file -->
        <arg value="-font"/>
        <arg value="/path/to/font.ttf"/>
        <!-- font size -->
        <arg value="-pointsize"/>
        <arg value="14"/>
        <!-- keep the text in center -->
        <arg value="-gravity"/>
        <arg value="center"/>
        <!-- button label -->
        <arg value="label:${label}"/>
        <!-- button label -->
        <arg value="button.gif"/>
    </exec>

  • Generate thumbnails (great for automatically creating image galleries)

    <exec executable="${imagemagick.dir}/convert">
        <arg value="image.jpg"/>
        <arg value="-thumbnail"/>
        <!-- thumbnail size -->
        <arg value="150x150^"/>
        <arg value="-gravity"/>
        <arg value="center"/>
        <arg value="-crop"/>
        <arg value="150x150+0+0"/>
        <arg value="+repage"/>
        <arg value="thumb.jpg"/>
    </exec>

Categories: Software Tags: ,