Calling for ideas about builing image thumbnails
Calling for ideas about builing image thumbnails
Dear all, this is the first time for me to post a topic in this forum.
I am currently working on a PSP photo viewer in which users can see the previews of their images (PNG and JPG) before they open them, just like the one in Sony's XMB. I propose the following algorithms to show the previews:
1.Read the WHOLE picture into RAM and select some pixels form it to make the thumbnail;
2.Read the image row by row but only take some pixels to make the thumbnail.
Unfortunately, the above methods are found to be very inefficient and resource-consuming especially for large images. Does anyone have better ideas, or know any functions in libpng and libjpeg that are useful in building thumbnails? Thanks.
I am currently working on a PSP photo viewer in which users can see the previews of their images (PNG and JPG) before they open them, just like the one in Sony's XMB. I propose the following algorithms to show the previews:
1.Read the WHOLE picture into RAM and select some pixels form it to make the thumbnail;
2.Read the image row by row but only take some pixels to make the thumbnail.
Unfortunately, the above methods are found to be very inefficient and resource-consuming especially for large images. Does anyone have better ideas, or know any functions in libpng and libjpeg that are useful in building thumbnails? Thanks.
some jpg are progressive encoded, so they have already a thumbnail.
once you made the tumbnail, you could save it in the same place and next time just load it (most pictureviewer I know have this kind of caching), so it won't be that bad.
btw. I'd do methode 2 to generate the images, as some decompressed jpg might requiere more memory than the psp has.
once you made the tumbnail, you could save it in the same place and next time just load it (most pictureviewer I know have this kind of caching), so it won't be that bad.
btw. I'd do methode 2 to generate the images, as some decompressed jpg might requiere more memory than the psp has.
I have just implemented method 2 using libpng to generate thumbnail images with sides 64px, and here is the codes.
What I observe is that the time taken for it to generate a thumbnail is almost the SAME(some may longer) as reading the whole image. It is not surprising as I think each generation needs to read through the whole image. I wonder how SONY's photo viewer in XMB reads thumbnails, it can just finish all in a split second!!
Code: Select all
//do some preparations here...
float SkipX=(float)ImgWidth/64,
SkipY=(float)ImgHeight/64,
TargetX=0, TargetY=0;
int StepX=0, StepY=0, ThumbX=0, ThumbY=0;
for(y = 0; y <ImgHeight; y++)
{
png_read_row(png_ptr, (u8*)tempLine, png_bytep_NULL); //read a row
if (y==StepY)//if the row is the one we want
{
//select some pixels from the row
for (TargetX=0, ThumbX=0; TargetX<(float)ImgWidth; TargetX+=SkipX, ThumbX++)
{
StepX=RoundOff(TargetX);
ThumbData[ThumbY*64+ThumbX]=tempLine[StepX];
}
//next row
ThumbY++;
TargetY+=SkipY;
StepY=RoundOff(TargetY);
}
}
You code is reading through the entire image, so it's always going to take at least as long. It sounds like you really need to cache the thumbnails you generate.Tong wrote:What I observe is that the time taken for it to generate a thumbnail is almost the SAME(some may longer) as reading the whole image. It is not surprising as I think each generation needs to read through the whole image.
In terms of minimising the time spent generating the thumbnail data, you could use the graphics hardware just to blit from the source image down to your desired thumbnail size? It you do this, you'll also get bilinear filtering for free (or close to free) and it'll look much better than the point sampling method you're currently using. It'll take a bit of fiddling with render targets, but I'm sure it'll be far faster than anything you could achieve with the cpu.
If you want to keep your UI responsive while generating the thumbnails you could also look at shifting thumbnail generation to a separate thread?
You can use the GPU to reduce your image size but it requires to load it entirely, and thus it's slow. You'd have to do it in a parallel thread I guess. Example (with OSLib):
Edit: sorry, my answer is not really helpful seeing what StrmnNrmn posted :-'
Code: Select all
//Creates a 64x64 image from an file
OSL_IMAGE *loadThumbnail(const char *filename) {
OSL_IMAGE *src, *dst, *oldDrawBuf = oslGetDrawBuffer();
int oldBilinear = osl_bilinearFilterEnabled, oldDither = osl_ditheringEnabled;
src = oslLoadImageFile(filename, OSL_IN_RAM, OSL_PF_8888);
dst = oslCreateImage(64, 64, OSL_IN_VRAM, OSL_PF_5650);
if (src && dst) {
oslSetDrawBuffer(dst);
oslSetBilinearFilter(1);
oslSetDithering(1);
src->x = src->y = 0;
src->stretchX = 64;
src->stretchY = 64;
oslDrawImage(src);
oslSetDrawBuffer(oldDrawBuf);
oslSetBilinearFilter(oldBilinear);
oslSetDithering(oldDither);
}
if (src)
oslDeleteImage(src);
return dst;
}
Last edited by Brunni on Sun Apr 22, 2007 9:34 pm, edited 4 times in total.
Sorry for my bad english
Oldschool library for PSP - PC version released
Oldschool library for PSP - PC version released
libpng and libjpg are not really fast. e.g. the intelJpgLib (no, there is no version for psp ;) ), could read about 20 320*200 jpg images per second on a Pentium200MMX (if I remeber correctly). so, if you want it faster, you'd have to improve the speed of the libs.Tong wrote:I wonder how SONY's photo viewer in XMB reads thumbnails, it can just finish all in a split second!!
Afaik, some picture-viewer (lieg irfanview) cache the next image in advance. so if you step to the next image, you already have it.
you'd not even need threading.
currently you do something like:
Code: Select all
wait for key
if key==next then
load next image
show next image
Code: Select all
wait for key
if key==next then
show next image
load next->next image
memorize next->next as next image
My day job deals with nothing but photos, so I've got some experience here :)
My suggestion is to go with epeg (http://www.enlightenment.org/Libraries/Epeg/) (or hack it up and change it to your liking like I did). It is incredibly fast at what it does, and combined with a few tricks (such as sampling at powers of two larger than what you want and then scaling down) you can get thumbnails that look just as good as ones taken from the real image.
Edit: epeg uses libjpeg. I haven't seen a PSP port, but it is POSIX-based and when I compiled it on OS X I didn't have to make any changes.
My suggestion is to go with epeg (http://www.enlightenment.org/Libraries/Epeg/) (or hack it up and change it to your liking like I did). It is incredibly fast at what it does, and combined with a few tricks (such as sampling at powers of two larger than what you want and then scaling down) you can get thumbnails that look just as good as ones taken from the real image.
Edit: epeg uses libjpeg. I haven't seen a PSP port, but it is POSIX-based and when I compiled it on OS X I didn't have to make any changes.
hmm... if it rely on libjpeg, then the only improvement is the downsampling speed, but I guess the decompression with huffman and DCT ist the really time consuming stuff.noxa wrote:Edit: epeg uses libjpeg. I haven't seen a PSP port, but it is POSIX-based and when I compiled it on OS X I didn't have to make any changes.
sounds like you'd be watching playboy all day long ;););)noxa wrote:My day job deals with nothing but photos, so I've got some experience here :)
Wrong. It only reads the lines from the image it needs for the desired size image. As long as the underlying file APIs are smart enough (and from what I've seen of newlib, they should be), a lot of file IO can be saved. In my particular use of epeg, I was loading large (3k x 3k) jpegs on crappy macs with slow hdds, and saw a large performance increase (talking of orders of magnitude in terms of time required to load/decode images). Going from loading 3000 lines 3000 pixels long to 128 lines 3000 pixels long is a big win. Not only in file IO, but in memory - on a device like the PSP it would be impossible to load an image of that size - there is not enough RAM to hold the decoded contents. epeg does a good job keeping memory usage during decoding low (and with some modification it could be made much better).rapso wrote:hmm... if it rely on libjpeg, then the only improvement is the downsampling speed, but I guess the decompression with huffman and DCT ist the really time consuming stuff.
This is exactly the kind of stuff that was mentioned above, only epeg works and deals with all the nasty stuff for you.
Funny.....?rapso wrote:sounds like you'd be watching playboy all day long ;););)
How about that...
edit: and maybe decompress the temp line thing to VRAM, it should be a little faster than RAM too. Anyway, to get the best speed possible your best bet would probably be to hack the read_row code in libpng.
Code: Select all
for(y = 0; y <ImgHeight; y++)
{
png_read_row(png_ptr, (u8*)tempLine, png_bytep_NULL); //read a row
if (y==StepY)//if the row is the one we want
{
//select some pixels from the row
Code: Select all
for(y = 0; y <ImgHeight; y++)
{
if (y==StepY)//if the row is the one we want
{
png_read_row(png_ptr, (u8*)tempLine, png_bytep_NULL); //read a row
//select some pixels from the row
Let's see what the PSP reserves... well, I'd say anything is better than Palm OS.
Code: Select all
for(y = 0; y <ImgHeight; y++)
{
if (y==StepY)//if the row is the one we want
{
png_read_row(png_ptr, (u8*)tempLine, png_bytep_NULL); //read a row
//select some pixels from the row
A big thanks to all who had give ideas to me especially noxa and rapso. I have spent a couple of days hacking into libepeg as recommended by noxa and I am now making use of it. Well, this library is really fantastic as noxa said, it can generate thumbnails at a terribly high speed!
As there no libepeg for psp up to now, let me share how I compile and use it with those who are interested at this useful library.
1.Download and install libjpeg (necessary, libepeg relies on it).
2.Download libepeg form http://enlightenment.freedesktop.org/fe ... 007.tar.gz
3.Unpack the package and go into src/lib directory.
4.Open “epeg_private.h” and remove line 16 “#include “config.h”” as it is useless to us.
5.“make” it using the following Makefile:
6.If successful, you should find Epeg.h and libepeg.a in your psp dir. You can use it now.
As there no libepeg for psp up to now, let me share how I compile and use it with those who are interested at this useful library.
1.Download and install libjpeg (necessary, libepeg relies on it).
2.Download libepeg form http://enlightenment.freedesktop.org/fe ... 007.tar.gz
3.Unpack the package and go into src/lib directory.
4.Open “epeg_private.h” and remove line 16 “#include “config.h”” as it is useless to us.
5.“make” it using the following Makefile:
Code: Select all
PSPSDK=$(shell psp-config --pspsdk-path)
PSPDIR=$(shell psp-config --psp-prefix)
TARGET_LIB = libepeg.a
OBJS = epeg_main.o
INCDIR = $(PSPSDK)/../include
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
LIBS = -ljpeg
include $(PSPSDK)/lib/build.mak
install: $(TARGET_LIB)
@echo "Installing libepeg into $(PSPDIR)"
@mkdir -p $(PSPDIR)/include $(PSPDIR)/lib
@cp Epeg.h $(PSPDIR)/include
@cp libepeg.a $(PSPDIR)/lib
@echo "Done"
Here is a simple example for outing a 128x96 thumbnail.
And its Makefile.
See the scary speed? Note: if we read the entire file into RAM and open it using Epeg_Image *epeg_memory_open (unsigned char *data, int size), the thumbnailing speed can be further increased.
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <pspkernel.h>
#include <pspdebug.h>
#include <Epeg.h>
PSP_MODULE_INFO("epeg", 0, 1, 1);
#define printf pspDebugScreenPrintf
int main()
{
pspDebugScreenInit();
//paste codes for setting up callbacks here
Epeg_Image *MyImage;
printf("Opening and reading info.\n");
if ((MyImage = epeg_file_open("Input.jpg"))==NULL)
{
printf("Cannot find Input.jpg, exiting...");
sceKernelDelayThread(2000000);
sceKernelExitGame();
}
int width, height;
epeg_size_get(MyImage, &width, &height);
printf("Original dimemsion: %dx%d\n", width, height);
epeg_decode_size_set(MyImage, 128, 96); //key point: size of thumbnail
epeg_quality_set (MyImage, 85);
epeg_thumbnail_comments_enable (MyImage, 1);
epeg_comment_set (MyImage, "thumbnail");
printf("Encoding and saving thumbnail...\n");
epeg_file_output_set (MyImage, "Output.jpg");
epeg_encode (MyImage); //this creates the file
epeg_close (MyImage); //free all allocated resources
printf("Done. Exiting...");
sceKernelDelayThread(1000000);
sceKernelExitGame();
return 0;
}
Code: Select all
TARGET = Epeg
OBJS = main.o
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
LIBS = -lepeg -ljpeg
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = libepeg sample
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak