Silverfrost Forums

Welcome to our forums

Scaling JPEG images

28 Apr 2009 12:09 #4483

When I create an offscreen grapics region, import a JPEG image and then transfer it to the screen, I have the following problems:

  1. Scaling the size down wrecks the image quality. I am trying to create thumbnails. Increasing the scale is OK.
  2. The graphics window handle, which I believe is 'User supplied', changes to zero during the existence of the window, and back to its original value when the window is destroyed. This means that routines which reference the window handle do not work and a separate handle is required.
  3. The original image downloads from the camera fails to be accepted. It is necessary to open the image in another program and re-save it. I used 'Irfanview' which is great for batch processing purposes. Camera was KonicaMinolta Z5.

The link below gives the source, compilation listing, project and JPEGs.

Regards

Ian

http://norsoftdev.com/silverfrost/offshorephotos.zip

28 Apr 2009 2:10 #4487

I don't have time to look at your code right now but I jsut want to check if you know about the routine set_jpeg_quality@. Having said this, it probably is not relevant to your needs.

28 Apr 2009 5:19 #4489

Paul, I have used set_jpeg_quality@ in the past, but the documentation suggests it is for exporting images to a file. My program imports an image from a file and displays it. It is OK for a scale of 1:1 and above, but not scaling down.

Regards

Ian

2 Jun 2009 11:23 #4652

Paul,

Just wondering if you could look at this one sometime.

Regards

ian

3 Jun 2009 1:04 #4662

Ian

I doubt that item 1 can be resolved in a reasonable amount of time. I was responsible for importing all of the JPEG stuff into ClearWin+ and the algorithms are very complex.

Given the current pressures on my time, I may only be able to investigate items 2 and 3 on a consultancy basis. If you are interested in this then please contact Silverfrost in the first instance.

Regards

Paul

3 Jun 2009 3:08 #4664

Paul,

Thanks for your response, my comments on the items are:

  1. The scaling is carried out using COPY_GRAPHICS_REGION@. Is this just a link to a Microsoft API? If so, it may be that at fault and it may not be able to shrink the image in the same way that it can stretch an image. There may be another way to process the jpeg to shrink it which I would be pleased if you could point me to. Such as GET_DIB_BLOCK@ and DISPLAY_DIB_BLOCK@ and to manually select/average the pixels from the imported array to reduce the size as necessary and create a new array, even though this could be process intensive. Or are you aware of another API function?

  2. I think that this is a bug in either the software or requires a comment in the help. The handle acts differently depending on whether it is a screen or printer/offscreen area.

  3. KonicaMinolta no longer make cameras and it would be much cheaper for me to buy a new camera or to keep using Irfanview. The latter option being the most likely.

Items 2& 3 were informative.

Regards

Ian

8 Jun 2009 9:34 #4678

Ian,

I remember playing wih some forum-user's code that resized a graphic - I'm sure it was a JPEG - but although I just went through the forum looking for the thread I couldn't find it. He was getting corruption, and it was solved by modifying the algorithm for computing new pixel colours.

I wonder if you have tried this with, say, a BMP file. If it works, then there is something in the handling of JPEGs, if it doesn't, then it is in COPY_GRAPHICS_REGION@. In any case, if you do repeated JPEG compression/decompression on an image the colours rapidly go haywire.

The handle issue is answered by Paul in his reply to my thread entitled 'When is a handle not a handle?' - and I found several other threads on the same theme in my hunt for that elusive code.

Buying a new camera isn't much of an answer - they all do JPEGs. I also have a Minolta. It will do quite low pixel-count images if you set it right. My newer cameras don't - they won't do a 640x480 (VGA) image for instance. A 640x480 image can be quite detailed.

Eddie

8 Jun 2009 10:29 #4679

Eddie,

I have just tried .BMPs instead - same result I'm afraid.

Thanks for that info about handles and hopefully the help will catch up eventually.

I'll have to think about another way of scaling down.

Regards

Ian

9 Jun 2009 12:08 #4682

Hi Ian,

Scaling down a coloured image is a matter of transforming from an array mxnx of cells ab in size with RGB colour values for each cell to a smaller array nxny of cells cd in size with RGB colour values related in some way to the original grid. If the aspect ratio remains the same, especially if a=b and c=d (so that the cells are square), the problem is simplified. In any case, it is simply a matter of filling nx*ny with appropriate colour values.

The simplest way of doing this is to operate on the centres of the cells. The new cell colour is the colour of the old cell whose centre is closest to the centre of the new cell. There are then 3 cases: simplest is where there is only one choice. That guarantees no colour change in the image, because only old colours are used. However, you may have 2 or 4 old cell centroids equidistant. Again, if you opt for one of the 2 or 4, there can be no colour shift. Alternatively, you can take an average. In that case, you must decompose the RGB values into R, G & B, and average each before reassembling them, taking care that each final component is a number between 0 and 255 inclusive.

If you want to be more sophisticated, you can average the R, G & B values of all old cells overlapped by a new cell, perhaps on the basis of area appearing in the new cell. The new cell RGB colours are then unavoidably different to the old image. You might minimise this shift by weighting large areas more than small ones.

If you have a background in finite element theory (an informed guess on my part), you might consider using shape function interpolation, treating the old grid as nodes defining square elements, and each new grid node as a point within a particular element. However, individual colour components need to be interpolated separately and recombined afterwards. Lagrangian interpolation is a more sophisticated alternative that would take into account more than the four nearest old cell centroids. If you haven't worked with finite elements, then ignore this paragraph!

Regards

Eddie

9 Jun 2009 12:28 #4683

Eddie,

Thanks very much, that is a great idea using the FEA mapping function. I will have a think about it. I think that c & d would always be 1 in my case, and a & b would be equal. I am only interested in a rough but colour-realistic thumbnail so it might be quite simple. As scaling up seems to work, I could pre-process the input picture to a standard size, maintaining aspect ratio and then pass the result through a standard reduction algorithm so that the thumbnails are always the same final size.

I'll let you know when I summon up the energy to have a go.

Regards

Ian

9 Jun 2009 12:46 (Edited: 10 Jun 2009 8:02) #4684

Ian,

I knew you were happy with FEA! Use 4-noded serendipity elements. The xi and eta coords come from the shift between the old grid and the new. You don't need to worry about coordinate transformation and Jacobian determinants etc. because the old grid is rectangular ...

Eddie

10 Jun 2009 2:37 #4685

Ian,

I tried the example photo you supplied in your download and saw the problem when reducing the size. It is not very good.

I'd be surprised if this problem is as difficult as requiring Lagrangian interpolation when scaleing down. I use a palette of 120 graded colours for my shadeing of stress fields from Lagrangian elements and that looks more than adequate (to me).

I think a test using your own colour interpolation with a weighted average of red, green and blue seperately, with the weighting based on the area proportion would work well. The eye makes colour rendering fairly forgiving.

As for when expanding, I'm sure the interpolation being used is not that complex either.

I'd be interested in hearing what you find.

John

10 Jun 2009 8:01 #4686

John,

I wouldn't use higher-order Lagrangian interpolation in the first instance either ... the linear serendipity element shape function IS the same as a first order Lagrangian one.

Since learning about shape functions in c. 1971 I have used them for all sorts of interpolation.

My first shot was 'use the colour of the (spatially) nearest old pixel' which is even simpler.

Eddie

10 Jun 2009 8:30 #4687

Eddie and John,

I was thinking that interpolation is perhaps more appropriate to increasing the density of an image. As I want to reduce it, an averaging technique may be more appropriate. I could of course be talking rubbish, a hobby of mine.

I'm still thinking about it.

Ian

10 Jun 2009 9:10 #4688

Hi chaps,

I've just had a go and simply picked the centre pixel from a rectangular area of the original image. If the thumbnail size is say 60 wide, then just step through the original image in 60 steps. Similarly in the vertical, create a new array with the reduced set and display it.

No interpolation and no averaging. It just seems to work.

Thanks for the hints and here is the code.

Ian

PS If the image is a line drawing, it is easy to miss narrow vertical and horizontal lines by this method, and perhaps the averaging method should be applied.

      WINAPP
      INCLUDE <windows.ins>
      CHARACTER*256 file
      integer*1, allocatable a(:,:,:)
      integer*1, allocatable b(:,:,:)
      INTEGER hres,vres,nb_colours,ier,i,k,control,hres_new,vres_new
!c---  Note - you may need to alter this path
      file='PICT2115.bmp'
      CALL get_dib_size@(file,hres,vres,nb_colours,ier)
      print *,hres,vres,ier,nb_colours
      allocate (a(3,hres,vres))
      CALL get_dib_block@(file,a,hres,hres,0,0,hres,vres,0,0,ier)
      IF(ier.NE.0) STOP 'TROUBLE'
!allocate the thumbnail with the same aspect ratio as the original and the biggest size
! as 256 bits wide
      imax_thumb_size = 60
      if(hres .gt. vres)then
        hres_new = imax_thumb_size
        vres_new = vres*imax_thumb_size/hres
      else
        vres_new = imax_thumb_size
        hres_new = hres*imax_thumb_size/vres
      endif
      print *,hres_new,vres_new
      allocate (b(3,0:hres_new-1,0:vres_new-1))
      ixratio = float(hres)/hres_new
      iyratio = float(vres)/vres_new
      ipickx_offset = ixratio/2
      ipicky_offset = iyratio/2
      print *,ixratio,iyratio,ipickx_offset,ipicky_offset
      do ix = 0,hres_new-1
        ix_orig = ipickx_offset + ix*ixratio
        do iy = 0,vres_new-1
          iy_orig = ipicky_offset + iy*iyratio
          b(1:3,ix,iy) = a(1:3,ix_orig,iy_orig)
        enddo
      enddo
!c---  Display the image
      i=winio@('%gr%lw',800,800,control)
      CALL display_dib_block@(0,0,b,hres_new,vres_new,0,0,hres_new,vres_new,0,0,ier)
      deallocate (a,b)
      END
11 Jun 2009 3:48 #4689

Ian,

I do all my graphics drawing/painting in a %gr window, by manageing my own virtual screen. Each pixel has 4 attributes; red, green, blue and depth, all stored in my common block. This is to handle hidden line removal and some other effects. The surfaces I draw are limited to flat polygons with linearly varying values of RGBZ. It is not as efficient/quick as opengl, but is fairly quick, at about 10 to 20 frames per second, for the type of graphics I draw (FEA). For photos it would be a bit slower. The important point is that it does work fairly well. You could develop your own image definition in memory(RGB), then convert it and dump it. To speed up the dump process, rather than process pixels, I look for lines of similar colour. This works very well for my FEA images, but would be less effective for photos. ( I think you can also get access to the %gr memory array via USER_SURFACE which would speed it up significantly. I'll have to look at than one day. )

I've found this approach very useful.

John

11 Jun 2009 6:52 #4690

Ian,

I have now looked at your latest example program and it works well. One of the strengths of this simple algorithm is that the colour chosen is a colour that is present in the original picture and not a modified colour that is probably not present, if an averageing method is chosen.

John

11 Jun 2009 11:40 #4693

Hi Ian,

Your solution follows my first suggestion (use colour of nearest old pixel). I'm glad it works.

Interpolation suggestions were for the situation that the result wasn't good enough. Mention of Lagrangian interpolation was to frighten you off doing anything complicated!

Every time we get a really satisfactory solution to a problem, it gets lost in the general morass of questions and unsatisfactory answers in these forums. Wouldn't it be a good idea to have a repository of good solutions?

Eddie

11 Jun 2009 12:22 #4694

I'll second that Eddie!

An online library of useful source code routines would be good, rather than have some vague recollection that I once saw a solution posted to that problem in some thread you have forgotten the name of!

Please login to reply.