Recent

Author Topic: How to save DPI jpeg  (Read 782 times)

DEN1983

  • New Member
  • *
  • Posts: 13
How to save DPI jpeg
« on: May 06, 2025, 10:22:57 am »
Hi everyone.
I have a source jpeg that has a DPI((Dots Per Inch) of 300 in X and 300 in Y.
It opens like this:
Code: Pascal  [Select][+][-]
  1. var  ThumbnailJPG: TBGRABitmap;
  2. ....
  3. ThumbnailJPG := TBGRABitmap.Create(FotoNAS);
After saving the image:
Code: Pascal  [Select][+][-]
  1. ThumbnailJPG.SaveToFile(FILEIMG);
DPI is 96
How to save 300 DPI as the original image.

wp

  • Hero Member
  • *****
  • Posts: 12787
Re: How to save DPI jpeg
« Reply #1 on: May 06, 2025, 02:23:52 pm »
I don't know whether BGRABitmap can handle EXIF. But you certainly could try the FPExif library (https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/fpexif/ - download by SVN or click "Download snapshot"):
First, extract the meta data from the original file and save them (imgData). Then load the image into BGRABitmap and do you image manipulations, save. Finally save the stored meta data back to the same file.
Code: Pascal  [Select][+][-]
  1. uses
  2.   fpeMetadata;
  3. var
  4.   imgInfo: TImgInfo;
  5. begin
  6.   imgInfo := TImgInfo.Create;
  7.   try
  8.     // Read meta data from file
  9.     imgInfo.LoadFromFile(orig_file);
  10.     // ---- load image into BGRA, manipulate, save
  11.     imgInfo.Save;
  12.   finally
  13.     imgInfo.Free;
  14.   end;
  15. end.

DEN1983

  • New Member
  • *
  • Posts: 13
Re: How to save DPI jpeg
« Reply #2 on: May 08, 2025, 11:32:58 am »
It doesn't work.
The image loses its quality already at the stage of loading into the TBGRABitmap class variable.
Code: Pascal  [Select][+][-]
  1. ThumbnailJPG := TBGRABitmap.Create(FotoNAS);
How can I upload an image to a TBGRABitmap class variable without losing its original quality!?

circular

  • Hero Member
  • *****
  • Posts: 4409
    • Personal webpage
Re: How to save DPI jpeg
« Reply #3 on: May 09, 2025, 06:11:23 pm »
Hi DEN1983,

Loading and saving the resolution has be implemented in recent versions. As far as I've tested it, the resolution is loaded and saved in Jpeg files. Can you provide a test project and the version you're using?

Regarding the quality, the JPEG reader in BGRABitmap uses the best quality by default. However the reader itself is herited from FPC.

Can you provide a screenshot to see the issue?

This could be reported there about TFPReaderJPEG: https://gitlab.com/freepascal.org/fpc/source/-/issues

Regards
Conscience is the debugger of the mind

DEN1983

  • New Member
  • *
  • Posts: 13
Re: How to save DPI jpeg
« Reply #4 on: May 09, 2025, 10:09:51 pm »
Hi DEN1983,

Loading and saving the resolution has be implemented in recent versions. As far as I've tested it, the resolution is loaded and saved in Jpeg files. Can you provide a test project and the version you're using?

Regarding the quality, the JPEG reader in BGRABitmap uses the best quality by default. However the reader itself is herited from FPC.

Can you provide a screenshot to see the issue?
circular hello!
Help me!

I have photos from a NIKON Z9 camera.
Properties of the source images:
- The image format is jpeg
- Width: 8256 pixels.
- Height: 5504 pixels.
- Horizontal resolution of 300 dots per inch.
- Vertical resolution of 300 dpi.
- 24-bit color depth.

I need to reduce this image while maintaining this original quality.

My code:
Code: Pascal  [Select][+][-]
  1. var
  2.         ThumbnailJPG: TBGRABitmap;
  3.         FotoNAS: String;
  4.         ThumbnailFoto: String;
  5.         MemoryStream: TMemoryStream;
  6.        
  7. ThumbnailJPG := TBGRABitmap.Create(FotoNAS);
  8. ThumbnailJPG.ResampleFilter := rfBestQuality;
  9. BGRAReplace(ThumbnailJPG, ThumbnailJPG.Resample(1816, 1211, rmFineResample, True));  
  10. ThumbnailJPG.SaveToFile(ThumbnailFoto);
  11. {
  12. or
  13. MemoryStream := TMemoryStream.Create();
  14. ThumbnailJPG.SaveToStreamAs(MemoryStream, ifJpeg);  
  15. }

FotoNAS - is the original image in jpeg format.
ThumbnailPhoto - is a thumbnail image in jpeg format.
After image reduction, horizontal resolution is 96 dots per inch, vertical resolution is 96 dots per inch.
https://disk.yandex.ru/d/Z1wMvk4YJfI2_Q

My BGRABitmap version is 11.6.4
« Last Edit: May 09, 2025, 10:20:00 pm by DEN1983 »

wp

  • Hero Member
  • *****
  • Posts: 12787
Re: How to save DPI jpeg
« Reply #5 on: May 09, 2025, 11:40:40 pm »
I don't know BGRA very deeply, therefore I am posting an example how I would do it with fpimage and fpexif - see attached project which loads your "original.jpg", saves the exif block, scales the image down to a width of 1000 px and copies the stored exif back into the new image (and adjusts the exif width and height entries). (Note that, because the original image is rotated by 270° according to exif, the size-reduced image would have the wrong orientation if exif would not be copied).

(fpexif is included in the attached demo project to prevent you from having to download this library).
« Last Edit: May 10, 2025, 12:16:22 am by wp »

DEN1983

  • New Member
  • *
  • Posts: 13
Re: How to save DPI jpeg
« Reply #6 on: May 10, 2025, 08:27:44 am »
I don't know BGRA very deeply, therefore I am posting an example how I would do it with fpimage and fpexif - see attached project which loads your "original.jpg", saves the exif block, scales the image down to a width of 1000 px and copies the stored exif back into the new image (and adjusts the exif width and height entries). (Note that, because the original image is rotated by 270° according to exif, the size-reduced image would have the wrong orientation if exif would not be copied).

(fpexif is included in the attached demo project to prevent you from having to download this library).
HI wp!
Thanks!
Your program is working fine, but still the quality is poor.
I want to get a high-quality thumbnail image.

Here is an example of the picture I need to get.
Made in FastStone Foto Resizer 4.4.
Pay attention to the size of the picture - 760 kilobytes!
https://disk.yandex.ru/i/1edb-MeAVOkgZA

Program settings FastStone:
« Last Edit: May 10, 2025, 08:50:04 am by DEN1983 »

wp

  • Hero Member
  • *****
  • Posts: 12787
Re: How to save DPI jpeg
« Reply #7 on: May 10, 2025, 10:01:46 am »
Thanks for finally defining "quality"  (you mean JPEGCompressionQuality=100 and Laczos interpolation).

In order to modify the compression quality in my code you must define an explicit writer instance and set the JPEGCompressionQuality to 100. I think this is the most important step to improve the quality of the compressed image. And in order to define the interpolation type you must create a specific instance for it in the image canvas. Many interpolations are implemented in unit extinterpolation, among them TLanczosInterpolation. I sometimes have the impression that Lanczos is buggy, but when I use the TFPBaseInterpolation instead I do not see much difference in the final output. Find screenshots of the magnified final result images in the attachment (left image: Lanczos/100%, center image: Base/100%, right image: default = Mitchell/75%)

Here is the modified code for the demo in my previous post:
Code: Pascal  [Select][+][-]
  1. program project1;
  2. uses
  3.   FPImage, FPCanvas, FPImgCanv, FPReadJpeg, FPWriteJpeg, ExtInterpolation,
  4.   fpeMetaData;
  5. var
  6.   srcFileName: String;
  7.   destFileName: String;
  8.   destSize: Integer;
  9.   factor: Single;
  10.   src: TFPMemoryImage;
  11.   dest: TFPMemoryImage;
  12.   canvas: TFPCustomCanvas;
  13.   imgInfo: TImgInfo;
  14.   writer: TFPWriterJPEG;
  15. begin
  16.   srcFileName := 'original.jpg';
  17.   destFileName := 'reduced-1000.jpg';
  18.   destSize := 1000;
  19.  
  20.   src := TFPMemoryImage.Create(0, 0);
  21.   try
  22.     src.LoadFromFile(srcFileName);
  23.     if src.Width > src.Height then
  24.       factor := destSize / src.Width
  25.     else
  26.       factor := destSize / src.Height;
  27.     imgInfo := TImgInfo.Create;
  28.     try
  29.       imgInfo.LoadFromFile(srcFileName);
  30.       dest := TFPMemoryImage.Create(round(src.Width * factor), round(src.Height * factor));
  31.       try
  32.         canvas := TFPImageCanvas.Create(dest);
  33.         try
  34.           canvas.Interpolation := TLanczosInterpolation.Create;
  35. //          canvas.Interpolation := TFPBaseInterpolation.Create;
  36.           try
  37.             canvas.StretchDraw(0, 0, dest.Width, dest.Height, src);
  38.           finally
  39.             canvas.Interpolation.Free;
  40.           end;
  41.           writer := TFPWriterJPEG.Create;
  42.           try
  43.             writer.CompressionQuality := 100;
  44.             dest.SaveToFile(destFileName, writer);
  45.           finally
  46.             writer.Free;
  47.           end;
  48.           imgInfo.ExifData.TagByName['Exif.ExifImageWidth'].AsInteger := dest.Width;
  49.           imgInfo.ExifData.TagByName['Exif.ExifImageHeight'].AsInteger := dest.Height;
  50.           imgInfo.SaveToFile(destFileName, destFileName);
  51.         finally
  52.           canvas.Free;
  53.         end;
  54.       finally
  55.         dest.Free;
  56.       end;
  57.     finally
  58.       imgInfo.Free;
  59.     end;
  60.   finally
  61.     src.Free;
  62.   end;
  63. end.

In BGRA, basically the same can be done, and probably with less lines of code. Just look at the sources and find out.

circular

  • Hero Member
  • *****
  • Posts: 4409
    • Personal webpage
Re: How to save DPI jpeg
« Reply #8 on: May 10, 2025, 11:08:45 am »
Ok so the issue is that the Resample function doesn't preserve the DPI and as wp said you need to configure the JPEG writer.

Code: Pascal  [Select][+][-]
  1. uses BGRAWriteJPEG...;
  2. var
  3.         ThumbnailJPG: TBGRABitmap;
  4.         Writer: TBGRAWriterJPEG;
  5.         FotoNAS: String;
  6.         ThumbnailFoto: String;
  7.         MemoryStream: TMemoryStream;
  8.         ResUnit: TResolutionUnit;
  9.         ResX, ResY: Single;
  10.        
  11. ThumbnailJPG := TBGRABitmap.Create(FotoNAS);
  12. ResUnit := ThumbnailJPG.ResolutionUnit;
  13. ResX := ThumbnailJPG.ResolutionX;
  14. ResY := ThumbnailJPG.ResolutionY;
  15. ThumbnailJPG.ResampleFilter := rfLanczos2; // you can try with 3 or 4 as well
  16. BGRAReplace(ThumbnailJPG, ThumbnailJPG.Resample(1816, 1211, rmFineResample, True));  
  17. // restore resolution
  18. ThumbnailJPG.ResUnit := ResolutionUnit;
  19. // actually, it would make sense to scale the resolution values because the pixel density has changed
  20. ThumbnailJPG.ResX := ResolutionX;
  21. ThumbnailJPG.ResY := ResolutionY;
  22. Writer := TBGRAWriterJPEG.Create;
  23. Writer.CompressionQuality := 100;
  24. ThumbnailJPG.SaveToFile(ThumbnailFoto, Writer);
  25. Writer.Free;
Conscience is the debugger of the mind

DEN1983

  • New Member
  • *
  • Posts: 13
Re: How to save DPI jpeg
« Reply #9 on: May 10, 2025, 02:11:44 pm »
circular and wp thank you very much!
Thanks to your advice, I wrote this code:
Code: Pascal  [Select][+][-]
  1. TargetJPG.ResolutionUnit := ruPixelsPerInch;
  2. TargetJPG.ResolutionX := 300;
  3. TargetJPG.ResolutionY := 300;
  4. WriterJPEG := TBGRAWriterJPEG.Create;
  5. WriterJPEG.CompressionQuality := 95;
  6. TargetJPG.SaveToStream(MemoryStream, WriterJPEG);  
  7.  

Everything is working as it should!
« Last Edit: May 12, 2025, 06:03:57 pm by DEN1983 »

circular

  • Hero Member
  • *****
  • Posts: 4409
    • Personal webpage
Re: How to save DPI jpeg
« Reply #10 on: May 13, 2025, 10:01:22 am »
Wonderful  :)
Conscience is the debugger of the mind

 

TinyPortal © 2005-2018