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?
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.

lopemCommented:
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;
0
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
0
lopemCommented:
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
0
Cloud Class® Course: Microsoft Exchange Server

The MCTS: Microsoft Exchange Server 2010 certification validates your skills in supporting the maintenance and administration of the Exchange servers in an enterprise environment. Learn everything you need to know with this course.

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) ?
0
lopemCommented:
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;
0
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

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

see methods of TJpegImage
0
lopemCommented:
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
0
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
0
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 ?
0
lopemCommented:
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.
0
LeTayAuthor Commented:
Sorry, think I did something wrong
Looking again ...
0
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...
0
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

0
lopemCommented:
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.
0
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 !
0
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
0

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 !
0
lopemCommented:
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
0
lopemCommented:
I think, Le Tay, you should gove the points to ther experts involved in your problem.
0
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

0
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
0
LeTayAuthor Commented:
Points to nobody then
0
lopemCommented:
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.
0
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
0
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
0
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
0
LeTayAuthor Commented:
The library found at Graphics32.org is wonderful
The decompressed image in BitMap is first vectorized and then manipulated
Result is excellent
0
LeTayAuthor Commented:
Attached is the result I obtain now with the Graphics32 library, with the JPEG I posted
graphics32.jpg
0
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.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.