A journey with resizing and cropping images in Java

2011 January 28
by Patrick Herrera

Numerous web applications we have developed over the years have had to handle the uploading and subsequent processing of images – user profile images are a perfect example.

In the old, old days when we developed in Perl this was quite easy – the PerlMagick bindings to ImageMagick made the whole process fast, simple and largely enjoyable.

Since moving to Java it became more complicated and more to the point, slower: image handling in pure Java was not blindingly fast if you wanted to handle reasonable sized images at an acceptable speed to the end user.

After experimenting with everything from basic AWT routines (slow) to invoking external scripts that wrapped ImageMagick (fast, but somewhat brittle and not easy to run on local development machines), we started looking at the Java Advanced Imaging API.
I’m not sure the best way to describe this API, as it is listed on the Sun (sorry … Oracle) website as part of the Java Media APIs, but is an optional component and is being developed as a “community source projects on java.net”.  My preference was certainly to try and use something ‘official’ – it seemed crazy to require yet another third-party library just to resize the occasional image, so this seemed to fit the bill.

The attractive part was that it provided native code support so hopefully the performance issues were a thing of the past.

Unfortunately programming against this API was even less intuitive than using AWT routines: this really is an API designed for image processing and doing the simple stuff ends up being complicated.

A quick search pulled up all sorts of examples of resizing images.  This blog in particular seemed to be a good start: Digital Sanctuary, and I got an example running easily.

Small is good, but I want BIG

However, a pretty serious limitation came up almost straight away: you are only able to apply scaling factors between 0 and 1 (well, I presume you can’t actually use zero as a scaling factor unless you want to create a black hole or something).

What if you want to INCREASE the size of the image?

Following Devon’s advice about changing the values of the ParameterBlock (which unfortunately wasn’t followed up with a working example from anyone), I started looking around on what this actually means.  His example for scaling down is simple enough:

ParameterBlock paramBlock = new ParameterBlock();
paramBlock.addSource(originalImage); // The source image
paramBlock.add(scale); // The xScale
paramBlock.add(scale); // The yScale
paramBlock.add(0.0); // The x translation
paramBlock.add(0.0); // The y translation

What I soon discovered is that ParameterBlock is the ’secret handshake’ of JAI developers, and they are doing their best to keep it out of the hands of mere mortals such as myself.  I don’t think I have ever come across something so opaque and utterly incomprehensible.

Try doing a Google search for “jai increase image scale” and see if anything comes up (besides this post obviously).

Anyway, after wasting a few hours I had to draw the line somewhere and move onto a different method.

Back to AWT and Java 2D

Reluctantly removing JAI from the equation, I broadened my search and soon came up with what is presented here in its final form:

/**
* Perform actual resize based on provided parameters.
*
* @param newWidth The width of the new image
* @param newHeight The height of the new image
* @param scaleX The amount to scale source image by in the X axis
* @param scaleY The amount to scale source image by in the Y axis
* @param source The source image to resize
* @return New BufferedImage being a scaled version of source
*/
private BufferedImage doResize(int newWidth, int newHeight, double scaleX, double scaleY,
                               BufferedImage source) {
  GraphicsConfiguration gc = getDefaultConfiguration();
  BufferedImage result = gc.createCompatibleImage(newWidth, newHeight,
                                                  source.getColorModel().getTransparency());
  Graphics2D g2d = null;
  try {
    g2d = result.createGraphics();
    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                         RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    AffineTransform at = AffineTransform.getScaleInstance(scaleX, scaleY);
    g2d.drawRenderedImage(source, at);
  } finally {
    if (g2d != null) { g2d.dispose(); }
  }
  return result;
}

You’ll notice that both the desired new size, and a scaling factor are passed in.  These are worked out earlier in the process and we need both depending on what we are trying to do.

This new routine uses the Java 2D API, which is an improvement over straight AWT when it comes to actually doing the rescale.  From the release notes (my emphasis):

As of J2SE 5.0, all images created with a BufferedImage constructor are now managed images and can be cached in video memory or, in the case of a remote X server, on the X server side. Previously, the Sun implementation managed only compatible images — those created with the Component createImage(int, int) method or with the GraphicsConfiguration createCompatibleImage methods. Managed images generally perform better than unmanaged images.

Most importantly, this could handle scale values greater than 1.  Using the VALUE_INTERPOLATION_BICUBIC constant seemed to give good results in terms of scaling quality, although the final compression used when writing out the image is just as critical to the process.

Now to keep the designers happy

With the ability to do good quality and rapid scaling up or down under our belts, it was time to address the next issue that keeps the designers awake: how do we take the odd shaped images that users thoughtlessly upload into the nicely designed placeholders that the designers have wasted sacrificed hours tweaking and laying out just so?

The answer is applying a crop to each image after applying the appropriate resizing.

This was shockingly easy:

BufferedImage newImage = sourceImage.getSubimage(xOffset, yOffset, sectionWidth, sectionHeight);

Thus we can create a new image of sectionWidth by sectionHeight from the original image, starting at offsetX, offsetY.  Okay, so technically this isn’t a crop as the original is unaffected, but you get the idea.

Putting it all together

The trick is working out what the scaling factors are based on the size of the original versus the desired size, and working out what to crop, which changes depending on whether you are uploading a tall image into a wide placeholder etc.

To achieve this, I built up a separate ‘ImageResizeRequest’ class that could be used to hold all the parameters to describe an image resize operation.  This was far neater that having to pass an unwieldy number of parameters to a method, and allows for the provision of sensible defaults.

Parameters includes:

  • Desired width and height
  • Whether to resize if larger, smaller, always or never (in which case the image is just cropped to the desired size, unless you request not to crop, in which case you should get back the image you originally provided – I’ve never tried it!)
  • The actual source image, either as an existing BufferedImage (so you could chain operations together), or a File object, or an absolute file path.
  • The destination file path
  • The compression factor to use in the resulting image
  • Whether to actually crop – you can elect to just resize to fit completely within requested dimensions which inevitably means it will be a different size in one dimension (I hear designers reaching for their aspirin at this point)
  • You can even elect to resize and NOT maintain aspect ratio.  ‘maintainAspectRatio’ is naturally true by default (hence previous point), but I can keep this up my sleeve to annoy the designers if needed

The actual work is done by a single ‘resize’ method defined in a separate ImageResizeService class.

We used Spring extensively and the use of services really appeals, so this was designed to be injected as a ‘Singleton’ Spring bean from day one and is annotated as such.

The logic to determine when and how to resize and crop was somewhat painful, especially as I didn’t want to do anything obviously silly like call a resize operation when not needed, or crop to the dimensions of the original image.

This logic was based on the Perl code used in our content management system, but then tidied up and fixed up over a frustrating hour or two.

Finally, a bunch of tests were put together to exercise various resize operations and check that things were scaled and cropped when they should.

Source and Example Usage

The source can be downloaded here. Note: you may want to remove the reference to Spring in ImageResizeService if you are not using that framework (you should be!).  Just remove the annotation and the import.

There are three source files (including an enumeration for the desired resize conditions) and a unit test.  I’ve also included two images which can be used as an input to the test.  There shouldn’t be any dependencies except a recent JDK and JUnit for the test.

The provided unit test has plenty of examples, but I’m sure you want to see how easy it is to use before wasting time downloading and opening a zip file:

ImageResizeService handler = new ImageResizeService();
ImageResizeRequest request = new ImageResizeRequest();

request.setSourceFilePath("d:\\input_image.jpg");
request.setTargetHeight(300);
request.setTargetWidth(300);
request.setMaintainAspect(true);  // This is the default
request.setCropToAspect(true);  // This is the default
request.setResizeAction(ImageResizeAction.IF_LARGER);  // This is the default
request.setDestinationFilePath("d:\\output_image.jpg");

handler.resize(request);

System.out.println("Image Cropped: " + request.isCropped());
System.out.println("Image Resized: " + request.isResized());

Pretty simple huh?

Note, the resize() method does throw a checked Exception, either from one of the underlying Java operations (for example if the ImageIO.read() method can’t read the image File it is provided, most likely because it is simply a format it cannot understand), or an application specific one such as not providing a targetWidth or targetHeight.

I made it checked so no-one got lazy and allowed an exception to reach the user in a web application environment.

Hints

The ImageResizeRequest is reusable across multiple invocations if you want to create multiple sizes of thumbnails (I think a designer just peed their pants with excitement).  The two status values of isCropped and isResized are guaranteed to be set correctly with each invocation.

If in doubt, create a new request from scratch if desired.

If you do want to create multiple thumbnails from a single image, do yourself a favour and take advantage of the fact that the resulting image is always returned by the resize() method (as well as written to disc if provided an optional destinationFilePath value).  Start with the largest thumbnail and generate smaller thumbnails from that.  Saves lots of time when users upload directly from their digital cameras for their avatar image.

A little gotcha is that if you say to resize IF_LARGER, and also set cropToAspect = true, it will not resize images to fill the space if they are only larger in one dimension.  Thus it is guaranteed to not increase the size of the image, which to me would violate the idea of resizing if larger.

For example uploading an image 200 wide by 500 high to a 300 by 300 target will crop the height to 300 pixels, but leave the width at 200 pixels.  If you wish to fill the space entirely, use resize ALWAYS.

To-do

I’m pretty happy with it.  The main limitation is that it is hardcoded to generate JPG images only, but this would be simple enough to change via a new ImageResizeRequest parameter later.

Also, cropping will currently crop top and bottom or sides equally as needed.  You might want a parameter to control cropping starting from a corner, but I didn’t bother as 99 times out of 100 it won’t be as good as going from the middle.

Conclusion

Well, I hope this helps someone navigate the maze that is simple image manipulation in Java.  Feel free to use as you see fit but we’d appreciate the link to http://www.futuremedium.com.au/ be retained in any application.

If this gathers any interest I’ll try and keep the source updated with any fixes or general niftyness.  Also tell me if the source disappears from our website – Wordpress wouldn’t let me upload it here for ’security reasons’.

Good luck!

There is a follow-up article to this: http://blog.futuremedium.com.au/2011/06/17/resizing-and-cropping-images-in-java-revisited/

Agile Digital Marketing – what’s the fuss?

2011 January 24
by Glen Johnson

OK.  Yes.  Even I’ve started to say these two words together a lot in the last 12 months. ‘Digital Marketing’….. But what’s all the fuss?

We’ve been digital marketers since day 1.  In our case that’s July 1st 1999.  We just haven’t called ourselves that.  So why does it feel so new to so many organisations?  Well it’s an elegant culmination of the pointy consumer / market focussed disciplines you’ll find in a digital agency. 

Let’s face it: plenty of us in the industry have had very specific hats on; strategy, creative, development, support, writing etc. and some of us have been focussing on Search Engine Optimisation (SEO) and Search Engine Marketing (SEM).  But if your client base has been internal web applications or a known entity such as a member base then you’ve likely been using most disciplines a digital marketer would except one: Acquisition.

Not to say that digital marketing is defined by acquisition but most certainly it is the focal point in my opinion.  Take brand exposure and community development for example, they’re clearly part of digital marketing too but we’re pushing a different wheel barrow here.

So what sorts of organisations need to take digital marketing seriously?  I know there’s confusion in the market right now….

Oh it’s facebook… I don’t need that! 

Or…

Oh yeah Twitter, I don’t want to risk playing with that.

Or what about this one….

SEO (SEM) that’s what it is – and those little ads on the side of Google – but that’s all a scam isn’t it?  They’re just charlatans stealing my money each month…

Or…

Banner ads – sheesh!  That’s so dot.com

It’s interesting how so many people think digital marketing is social media or it’s just SEM.  And I can see how this image has been built.  There’s a lot of opportunity to market an organisation digitally and trying to grasp all of this without some expertise is tricky.  Sorry I digress.

So who needs digital marketing? 

  • My Masseuse does: he needs a facebook page and he needs people to ‘like him’. 
  • My car service company does: so that I can be kept aware of service issues, recalls, upgrades, new parts etc. for my car via twitter or via email blasts. 
  • My insurance company does: they need to keep me aware of my choice being a good one and not a grudge purchase. 
  • My investments do: because I don’t want to always have to log in to my share platform to know what’s happening.

I could insert just about any organisation I deal with in this list and keep bashing out thoughts of what they could do.  And why could I do that?  Simple!  I’m one of those people that lives in a digital world and hates getting information pretty much every other way. 

No I don’t watch the news, no I don’t read the paper and God help me if I hear the radio on… GRRR I hate the radio (unless I’ve got a DJ set airing AND THAT’s DIFFERENT)

My point is that there’s more people like me that want to hear from others but only if it’s intelligent and relevant to them.  I don’t have the spare brain cells to leave around to soak up guff from mass media.  (Not to say that I’m stupid, can’t read, or listen, :) but I’ve made a conscious decision to limit my exposure to NOISE).  Others just don’t want to be hassled.

I think most organisations that haven’t embraced digital marketing in some way are nervous.  I regularly see something of a fear in marketing digitally and a concern about just wasting money.  And why is it then that for the past 6 months of meetings with clients I’ve found a persistent reluctance to do digital marketing activities in most of them (but some – a small percentage spend big bucks)?  For the non-believers the issues are:

  1. How will I give results?
  2. How will I justify the results in terms of budget spend?
  3. Are my customers/targets really online – will they just be poor ones that are conditioned to shop around and not be loyal to me?

An expert from the UK recently said to me:

“If I asked you for a million dollars to do digital marketing you’d laugh at me, but if I could prove that I would get you multiples of millions in terms of revenue you’d go for it wouldn’t you?”. 

Sometimes you have to think in extremes to get smaller points across.  And he’s right.  Damn straight I’d give him the money (if I had it…..but that’s another issue).  But he could prove to me access to a market through tests and then ramp up the pursuit of that market slowly such that my investment created sensible and scalable return.

So I’ve concluded that results driven digital marketing is the only way to bolster out the sector and recondition the market into trusting digital marketing. 

How do you do that?  Some good solid strategic work up front from a full team of experts across all issues in the digital sector and then a bunch of small projects that you test and learn from as you scale up your expenditure.

Recently we’ve been looking at agile methodologies being applied to digital marketing.  Small tests, small projects, small tweaks, small budgets and THEN BAM! Build it up when it is working.  This may not feel right to most marketers and certainly to most clients used to dealing with cyclical project based web developer relationships.  But you can burn money really quick with a poorly setup SEM campaign.

We’ve been working on a few digital marketing projects recently and doing our best to keep agile and ‘test and learn’.  It’s a significant culture shift for us despite adopting agile methods in our development where we can.

So don’t shrug it off next time someone asks you about it.  Just start from the results end first and make sure the person discussing it with you can think big enough to find a way to the result.

Google Instant – more need for SEO than ever?

2010 October 6
by Glen Johnson
tags:

Google Instant.  What does it mean to SEO work?  I reckon it increases the need to do lots of SEO work given the likelihood of deeper site hits.

Here’s a video: Google Instant introduction

Google are also spruiking the benefits in their own blog http://googleblog.blogspot.com/2010/09/search-now-faster-than-speed-of-type.html

What does it mean to a user’s search query if predictive text is used to propose a search query and as this query is typed the results dynamically appear in the search window without even pressing enter.  How does this shape their behaviour and consequently how does this change their path towards a result.  More options and quicker options.  Does that equall better chance of an accurate result or does it simply steer the result?

My immediate reaction was positive:

  • quicker searches
  • apparently more chance of getting the user a result, and
  • less interaction required to get results

Others not so positive, and I can see why.  Take real estate searches as an example: 

  • if Google starts to shape my query early on then maybe I’ll miss something seemingly unrelated on the way
  • maybe a query for a property in a particular suburb might take me on a tangent?  and
  • maybe away from an area that was traditionally optimised ‘for’ by the owner of the site

I don’t have a technical view on this yet. But when have we been able to question the power of Google’s search logic?  I can’t.

Google also say in their FAQ that ”this change does not impact the ranking of search results”.

Ultimately I feel that we have to see all improvements in terms of speed and efficiency for search queries as a positive as it must increase the number of avenues a user can go (look) down. 

In terms of everyone’s work on SEO for their sites I’d have to conclude that any work now is good work and the release of ‘Google Instant’ isn’t changing the need to do SEO in fact it probably increases the need to have greater relevance and ranking across much more than just your home page. 

So what’s my advice right now:

Make sure all your landing pages are optimised in their own right as better search query methods only increases the likelihood of deeper hits in your site (if you’re optimised).

EDIT / OCT 11th

I checked in with SEO experts Smart Traffic in the UK to see what they had to say about this.

Google Instant – What’s all the panic about?

 

By now you must all have heard of one of the newest Google updates, Google Instant. Not quite yet available in the UK & AUS for all, but what does it actually mean for the Search industry and SEO? During all the hype of the launch some people were saying it was the death of SEO, others disagreed. I am happy to say we are firmly in the latter group. From what I have seen reported it doesn’t appear to have had much affect at all so far. There are a few things though that we might expect to happen:

  1. More competition for generic keywords – as Google instant predicts what the user searches for as they type, highly searched generic keywords should appear first and savvy site owners will try and grab the traffic. This is great news for us as we have a fantastic track record for those extremely tough keywords e.g. laptop, lcd tv, cruise etc
  2. Possible decrease in the number of longtail keyword variations delivering traffic – if the above is true then you would assume that the longtail is dead as searchers find what they are looking for before completing a long and random search request. However, from what has been reported in the US market this may not be completely true
  3. Higher Bounce Rates – as more and more search results are shown in the front of the user you would expect more sites to be clicked on out of curiosity or by seeing something that may not have been their final search result but something that interested them.

Overall so far there appears nothing to be too alarmed about.

Top 3 is now more important than ever, so establishing yourself in the top 3 by quality linkbuilding is more important now than ever before.