PSOThumbnailGenerator creating grainy images

When I have Rhythmyx create thumbnails of images the results are coming out very grainy. I’m not expecting Photoshop quality resizing of images, but I was wondering if there is a setting somewhere that can be changed to adjust the compression/resize method?

What version of the product and build of the toolkit do you have?

At some point, we started using the new Java ImageIO library and the “bi-cubic” interpolation algorithm instead of the old JAI library. I believe that this is only available for 6.5.x.

If you’re still using 5.7, then you have the old library, and it’s not going to work as nicely.

Dave

We’re using 6.5.2 (53 for the build number of the toolkit).

That’s the new one. I don’t think we have anything better. What size thumbnails are you generating?

Some java code if you care to craft your own thumbnails:

package erau;
import com.sun.image.codec.jpeg.;
import java.awt.
;
import java.awt.image.*;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.ldap.;
import javax.naming.directory.
;
import javax.naming.;
import java.io.
;
import java.util.*;

public class ReSizeImages
{
public static void imageProcess(String infile, String outfile, int width, int height, int quality)
{
try
{
Image image = Toolkit.getDefaultToolkit().getImage(infile);
MediaTracker mediaTracker = new MediaTracker(new Container());
mediaTracker.addImage(image, 0);
mediaTracker.waitForID(0);
// determine thumbnail size from WIDTH and HEIGHT
int thumbWidth = width;
int thumbHeight = height;
double thumbRatio = (double)thumbWidth / (double)thumbHeight;
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
double imageRatio = (double)imageWidth / (double)imageHeight;
if (thumbRatio < imageRatio) {
thumbHeight = (int)(thumbWidth / imageRatio);
} else {
thumbWidth = (int)(thumbHeight * imageRatio);
}

  // draw original image to thumbnail image object and
  // scale it to the new size on-the-fly
  BufferedImage thumbImage = new BufferedImage(thumbWidth, 
    thumbHeight, BufferedImage.TYPE_INT_RGB);
  Graphics2D graphics2D = thumbImage.createGraphics();
  graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
  // save thumbnail image to OUTFILE
  BufferedOutputStream out = new BufferedOutputStream(new
    FileOutputStream(outfile));
  JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
  JPEGEncodeParam param = encoder.
    getDefaultJPEGEncodeParam(thumbImage);
  //int quality = Integer.parseInt(args[4]);
  quality = Math.max(0, Math.min(quality, 100));
  param.setQuality((float)quality / 100.0f, false);
  encoder.setJPEGEncodeParam(param);
  encoder.encode(thumbImage);
  out.close(); 

}catch(Exception e)
{e.printStackTrace();}

}

public static byte[] getFileData(String fname)
{
byte[] data = null;
RandomAccessFile rf=null;
try
{
rf = new RandomAccessFile(fname,“r”);
data=new byte[(int)rf.length()];
rf.readFully(data);
}catch(IOException e)
{e.printStackTrace();}
try{
if(rf!=null)
rf.close();
}catch(IOException e)
{e.printStackTrace();}
return data;
}

public static void WriteFileData(String strFilePath,byte [] data)
{
try
{
FileOutputStream fos = new FileOutputStream(strFilePath);
fos.write(data);
fos.close();
}
catch(FileNotFoundException ex)
{
System.out.println("FileNotFoundException : " + ex);
}
catch(IOException ioe)
{
System.out.println("IOException : " + ioe);
}
}

public static void main (String[] args)
{

String diffFile="c:\\images\\cid_map_jpg_diff.txt";
File file = new File(diffFile);
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;
System.out.print("Time: " + new Date());

try {
  fis = new FileInputStream(file);
  bis = new BufferedInputStream(fis);
  dis = new DataInputStream(bis);
  int count=0;    
  while (dis.available() != 0) {
    String buf = dis.readLine();     
    String[] record = buf.split(",");
    String cid = record[0];
    
    int width = 100; //max width in pixles
    int height=100;  //mas height
    int quality=100; //1-100
    String infile = "c:\\images\\" +  cid + ".jpg";//"C:\\photos\\bike.jpg"; //f.getPath();
    String outfile = "c:\\images\\" +cid + "_ldap.jpg";//"C:\\photos\\bike_th.jpg"; //f.getPath();
    imageProcess(infile, outfile, width, height, quality);

  }//while //      while (dis.available() != 0) {

  // dispose all the resources after using them.
  fis.close();
  bis.close();
  dis.close();           
} catch (FileNotFoundException e) {
  e.printStackTrace();
} catch (IOException e) {
  e.printStackTrace();
}//try
System.out.print("Time: " + new Date());

}
}

That’s the “old” way. The new way is to use the ImageIO library. Here’s what the current PSOThumbnailGenerator does:

  public void createThumbnail(OutputStream outstream, InputStream instream,  int maxDim) throws IOException
   {
      try {
         // Get the image from a file.
         BufferedImage inImage = ImageIO.read(instream);
         // Determine the scale.
         double scale = (double)maxDim/(double)inImage.getHeight(null);
         if (inImage.getWidth() > inImage.getHeight())
         {
            scale = (double)maxDim/(double)inImage.getWidth(null);
         }
         // Determine size of new image.
         // One of them should equal maxDim.
         int scaledW = (int)(scale*inImage.getWidth(null));
         int scaledH = (int)(scale*inImage.getHeight(null));

         // Create an image buffer in which to paint on.
         BufferedImage outImage = new BufferedImage(scaledW, scaledH,
                                                    BufferedImage.TYPE_INT_RGB);
         
         // Paint image.
         Graphics2D g2d = outImage.createGraphics();
         g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
               RenderingHints.VALUE_INTERPOLATION_BICUBIC);
         g2d.drawImage(inImage, 0, 0, scaledW, scaledH, null);
         //g2d.dispose();

         // JPEG-encode the image and write to output
         ImageIO.write(outImage, "jpeg", outstream); 
         
      }
      catch (IOException e) {
         log.error("Could not create thumbnail " + e.getMessage(), e);
      }
   }

You definitely want to use BICUBIC interpolation, it’s much much better than the old BILINEAR interpolation.

Excellent! Thanks for the sample code.

Is there any documentation on how to make Rhythmyx create the thumbnails automatically?
Or does anyone have a quick step-by-step of what needs to be done?

Thanks in advance,
Nick.

The Javadoc for PSOThumbnailGenerator (part of the PSO Toolkit which Technical Support can give you if you don’t already have it) should provide some help.

We’re creating thumbnails at 160, and 120.

I was working on the same project as Tim, and wanted to pass on our solution. The grainy thumbnails were caused because we were downscaling the images by more than 50%. When doing so, bicubic or bilinear resampling loses information because of how the pixels are sampled from the original image.

We ended up extending PSOThumbnailGenerator and overriding the createThumbnail method. Incrementally scaling the the image 50% at a time until reaching the desired dimensions eliminated the graininess. We based our code off an example found at http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html.

We still ran into problems for images with transparencies (the transparent areas would be converted to black) or ones using Nikon color profiles (the generated thumbnails were very dark). We’re contemplating switching to an implementation that uses ImageMagick (http://www.imagemagick.org/), which seems to handle these cases. In general, it seems to generate crisper thumbnails that more closely match the original colors, which sometimes get faded a bit in our implementation.

As it turns out, I’ve recently used this exact technique (downscaling repetitively by a factor of 2) for a different customer project.

One thing to watch out for: scaling very large images (over 1 MegaPixel) is extremely slow unless you use “NEAREST NEIGHBOR” algorithm. One of my test images took over 250 seconds to scale. So if you do scaling of large images, you want to use NEAREST NEIGHBOR if the image is really large and BILINEAR (or BICUBIC) once you’ve brought the image down to a reasonable size.

I’ll look at porting this back into the PSOThumbnailGenerator.

Thanks