Reduce a JPEG image size with Delphi components

Hello
I use the following code to reduce the size (keeping the same proportion) of a picture stored in a JPEG file
Unfortunately, for many of them the result is ugly (seems "pixelazed")
Is there a better code for this ?
(PixelsPhoto is the number of pixels to obtain in the resulting file)
 
  BMP  := TBitmap.Create;
  JPEG := TJPEGImage.Create;
  Temp := TImage.Create(nil);                    
  Temp.Picture.LoadFromFile(Source);  // Load the JPEG file
  Zoom2 := ((Temp.Picture.Height * Temp.Picture.Width) / PixelsPhoto);
   if (Zoom2 < 1.0) then Zoom2 := 1.0;
   Zoom         := SQRT(Zoom2);                 // Calcul zoom d'après # de pixels totaux max
    try
     BMP.Height   := Round(Temp.Picture.Height / Zoom); // Zoom
     BMP.Width    := Round(Temp.Picture.Width / Zoom);
     BMP.Canvas.StretchDraw(Rect(0,0,BMP.Width,BMP.Height),Temp.Picture.Graphic);
     JPEG.CompressionQuality := 100;
     JPEG.Assign(BMP);
     JPEG.SaveToFile(Destination);
    finally
     BMP.Free;
     JPEG.Free;
     end;
   end;

Open in new window

LVL 1
LeTayAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Manuel Lopez-MichelonePhD candidateCommented:
I do a reduce imagen procedure this way:

for i := 0 to Form1.Image1.Picture.width-1 do
  begin
    for j := 0 to Form1.Image1.Picture.height-1 do
    begin
          L := Form1.Image1.Canvas.Pixels[i,j];

          a := trunc(i * (SpinEdit1.Value / 100)); //50 is equal, for instace, a reduction of 50%
          b := trunc(j * (SpinEdit2.Value / 100));

          Form1.Image2.Canvas.Pixels[a,b] := L;
    end;
  end;
LeTayAuthor Commented:
What is SpinEdit ?
Is the result satisfactory ?
Can you post the result on the attached file ?
The file is the original I also attached "my" (ugly) result
00052-1.jpg
52-1.jpg
Manuel Lopez-MichelonePhD candidateCommented:
SpinEdit is a component in the delphi palette with the purpose o enter a number between a range, for example, 0-100 (0-100%) in the case of using the component to enter the porcentage of reduction of the image. I took your original image. This is the result. Y reduce the original imagen in 50%
52-2.jpg
Become a CompTIA Certified Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

LeTayAuthor Commented:
I do not understand very well
I do not see how SpinEdit1 and SpinEdit2 are initialised
Also, when you write reduce in 50 %, do you mean the size in pixels X and Y (so in fact, surface reduced to 25 %) or the weight (in bytes) ?
Manuel Lopez-MichelonePhD candidateCommented:
SpinEdit is just a component to input a number within a range. But if you dont understand this just change spinedit for the value you want to reduce:

for i := 0 to Form1.Image1.Picture.width-1 do
  begin
    for j := 0 to Form1.Image1.Picture.height-1 do
    begin
          L := Form1.Image1.Canvas.Pixels[i,j];

          a := trunc(i * (50 / 100)); //50 is equal, for instance, a reduction of 50% in X coordinate
          b := trunc(j * 50 / 100)); //50 is equal, for instance, a reduction of 50% in Y coordinate

          Form1.Image2.Canvas.Pixels[a,b] := L;
    end;
  end;
Geert GOracle dbaCommented:
lopem, pixel by pixel printing ? really ?

LeTay,
resizing only works well with bitmaps
first convert the jpeg to a bitmap
then resize/redraw on a secondary bitmap
then assign back to jpeg
then save

try this:
i used a spinedit to set the newwidth
you might need some extra magic to determine if you don't want to use the aspect ratio
or use the height as a determining factor

procedure TForm1.Button2Click(Sender: TObject);
var Source: TJPEGImage;
  Dest, Temp: TBitmap;
begin
  Source := TJpegImage.Create;
  try
    Dest := TBitmap.Create;
    try
      Temp := TBitmap.Create;
      try
        Source.LoadFromFile('D:\fotos\DSC01974.JPG');
        Source.DIBNeeded;
        Dest.Assign(Source);
        Temp.SetSize(seNewWidth.Value, Round(Source.Height * ( seNewWidth.Value / Source.Width)) );
        Temp.Canvas.StretchDraw(Rect(0, 0, seNewWidth.Value, Round(Source.Height * ( seNewWidth.Value / Source.Width)) ), Dest );

        Source.Assign(Temp);

        Source.SaveToFile('D:\fotos\DSC01974.TEST.JPG');
      finally
        Temp.Free;
      end;
    finally
      Dest.Free;
    end;
  finally
    Source.Free;
  end;
end;

Open in new window

LeTayAuthor Commented:
Will try all that
Geert GOracle dbaCommented:
the clue is in the DIBNeeded
> or JPEGNeeded
Depends on which direction you are converting too

see methods of TJpegImage
Manuel Lopez-MichelonePhD candidateCommented:
Hi, Geert Gruwez,

I teach digital image processing at the university and of course, I am not concerning about the speed of the algorithm. Instead, I just gave a simple solution to a simple problem ;) But of course, you're right, my code is sooo sloooow

regards
Manuel
LeTayAuthor Commented:
Hello gentlemen, keep cool
I am not currently concerned by performance but ... by the visual QUALITY of  the resulting image !
An the StretcHDraw method (I used it) seems to be the guilty in that domain
I will try the iopem one, from which I already receidved my posted image reduced and looking fine
LeTayAuthor Commented:
I tried your code, Iopem, using as example 50 % for X and Y
But the result is a very very small picture, even if I use 90 % ?!
Where is the trick ?
Manuel Lopez-MichelonePhD candidateCommented:
I don't know. I just copied the code I show to my students. I load the original image you gave us and ran my program. I am showing my result. By the way, I first loaded a bmp version of your image and then I did the reduction. next, I saved it as a jpeg file.
LeTayAuthor Commented:
Sorry, think I did something wrong
Looking again ...
LeTayAuthor Commented:
I get now an error at execution on this :
           L := Form1.Image1.Canvas.Pixels[i,j];
Related to non existing bitmap or something like that
Can you show me the code where you load  the source file eetc...
LeTayAuthor Commented:
I have adapted the code of Iopem that failed
Using directly TBitmap component of the FMW.Objects library, not the VCL (no need to display it on a form)
The result is exactly the same as the second alternative, with the StrechDraw
I am not surprized : the "broken" line of the lamp is due to the rounding (of truncation) in the process
This is unavoidable using such method
However, my daughter reduced that image using PhotoShop and the result is much better
I suspect PhotoShop to "try" some kind of "vectorization" and so that line remains a line
Or maybe I missed something ?
Here is my code (reducing factor is 10 % both axis) :

procedure ReduireBitParBit(Source,Destination:string);
var
 Image1,Image2:TImage;
 I,J,L:integer;
 A,B:integer;
 H,W:integer;
 H2,W2:integer;
begin
 if (not FileExists(Source)) then Exit;
 Image1 := TImage.Create(nil);
 Image2 := TImage.Create(nil);
 Image1.Bitmap.LoadFromFile(Source);
 H  := Image1.Bitmap.Height - 1;
 W  := Image1.Bitmap.Width - 1;
 H2 := Round(H / 10);
 W2 := Round(W / 10);
 Image2.Bitmap.Create(W2,H2);
 for I := 0 to W do
  begin
  for J := 0 to H do
    begin
      L := Image1.Bitmap.Pixels[I,J];
      A := Round((I * 10) / 100);
      B := Round((J * 10) / 100);
      Image2.Bitmap.Pixels[A,B] := L;
    end;
  end;
 Image2.Bitmap.SaveToFile(Destination);
 Image2.Free;
 Image1.Free;
end;

Open in new window

Manuel Lopez-MichelonePhD candidateCommented:
In my program I have two images, one is the source one and the second is the processed image. So I read from one image and write in the second one.
LeTayAuthor Commented:
Gentlemen,
Delphi library for images is not the top.
Reducing images use a simple method, shown by both of you.
Keeping only bitmap points based on original
There is a much better solution, outside of Delphi standard
The Graphics32.org library provided transforms the bitmap in vectors, reduce the picture and put it back as compressed bitmap
The result has nothing to see with Delphi provided objects and methods.
I propose to give the points to Graphics32.org !
Geert GOracle dbaCommented:
if you teach image processing, surely you have better algorithms than that

i remember a library for image processing ... i think by mike lischke
hmmm... seems it's only for loading
https://github.com/mike-lischke/GraphicEx

there is also good ol torry:
http://www.swissdelphicenter.ch/torry/showcode.php?id=1896

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
LeTayAuthor Commented:
I do not teach anything in that domain !
Manuel Lopez-MichelonePhD candidateCommented:
Geert Gruwez,
of course I have better algorithms. In fact, in a first review of many filters, I am not worried about the fastest algorithm. I wanto the students to understand what are they doing, why the filter works, etc...

regards
Manuel
Manuel Lopez-MichelonePhD candidateCommented:
I think, Le Tay, you should gove the points to ther experts involved in your problem.
Geert GOracle dbaCommented:
die you try playing around with smoothing and other properties ?

procedure TfrmPopup.Button1Click(Sender: TObject);
var Source: TJPEGImage;
  Dest, Temp: TBitmap;
  nw: integer;
  scale: double;
begin
  Source := TJpegImage.Create;
  try
    Dest := TBitmap.Create;
    try
      Temp := TBitmap.Create;
      try
        Source.LoadFromFile('d:\fotos\52-1.JPG');
        Source.DIBNeeded;
        Dest.Assign(Source);
        scale := 0.33;
        nw := Round(Source.Width * Scale);

        Temp.SetSize(nw, Round(Source.Height * ( nw / Source.Width)) );
        Temp.Canvas.StretchDraw(Rect(0, 0, nw, Round(Source.Height * ( nw / Source.Width)) ), Dest );

        Source.Assign(Temp);
        Source.Smoothing := True;
        Source.PixelFormat := jf24Bit;

        Source.SaveToFile('D:\fotos\52-test.jpg');
      finally
        Temp.Free;
      end;
    finally
      Dest.Free;
    end;
  finally
    Source.Free;
  end;
end;

Open in new window

Geert GOracle dbaCommented:
i have you have the image as a vector graphic,
then you should and maintain the vector graphic as long as possible

only vector graphics allow for correct scaling
the rest is an algorithm with more or less loss of quality
LeTayAuthor Commented:
Points to nobody then
Manuel Lopez-MichelonePhD candidateCommented:
Geert Gruwez did a lot for you. I dont really care about the points but I think you're unfair. Every expert who try to help you deserves some recognition from you after all.
LeTayAuthor Commented:
Well, the problem I had was not to reduce an image, as my code does that since years.
The question was related to this statement I wrote : " Unfortunately, for many of them the result is ugly (seems "pixelazed")"
Geert will get the points anyway
Geert GOracle dbaCommented:
LeTay,
that's just the point, there is no algorithm which doesn't have loss of image quality

you know, the movies where they do enhancements of number plates in police crime scenes ?
that's really a very good picture which gets reduced in quality and gets recorded into a film
and then they play that section backwards

there is no algorithm which can enhance course images
that would mean, you could reduce a 10x10 pixel image and enhance to a full blown picture

the smaller the size gets, the more quality and detail you loose
just a fact of life
Geert GOracle dbaCommented:
i'd keep the pics to a size where you have good quality
and then stretchdraw them in an image frame on screen

in time, people get better displays with higher resolution,
and without doing anything, your on screen display will be better
LeTayAuthor Commented:
The library found at Graphics32.org is wonderful
The decompressed image in BitMap is first vectorized and then manipulated
Result is excellent
LeTayAuthor Commented:
Attached is the result I obtain now with the Graphics32 library, with the JPEG I posted
graphics32.jpg
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.