Reading Width x Height from JPEG from Header (Or without loading into Image) lazarus / 39-Graphics ---------------------------------------------------------------------- zxandris · 2024-03-07 10:47 I assume this is possible, to get the width and height from the JPG header, but I have to admit I have no idea how to do this.  I'm hoping someone has done this before, or has an idea how to go about it? Any help would be appreciated, CJ ---------------------------------------------------------------------- Zvoni · 2024-03-07 11:14 https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format http://www.fastgraph.com/help/jpeg_header_format.html In a nutshell: The first two bytes are the SOI-Marker ("FF D8") The next two bytes are the width in pixels again the next two bytes are the height in pixels EDIT: Which doesn't mean above is correct...... ---------------------------------------------------------------------- Bart · 2024-03-07 11:19 You can use my picslib unit for that (function GetImageSize). (Notice that it requires several include files, which are available in the same folder.) Bart ---------------------------------------------------------------------- KodeZwerg · 2024-03-07 11:38 Complete native, limited to JPEG fileformat. Fastest method I am aware of. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TByteArr = array of Byte;   TJFIFSegment = packed record    Fix : Byte;    Kind : Byte;  end;   TSOFData = packed record    SamplePrecision : Byte;    Height : WORD; // Number of lines    Width : WORD; // Number of samples per line    Comp : Byte; // Number of image components in frame// Data : TByteArr;  end;  PSOFData = ^TSOFData; function ReverseWord(w: word): word;begin  Result := ((w shl  8) and $FF00) or ((w shr  8) and $00FF);end; function ReadWORD(FS : TFileStream; out AWord : WORD):boolean;begin  Result := (FS.Read(AWord,SizeOf(AWord)) = SizeOf(AWord));  AWord := ReverseWord(AWord);end; function ReadSegmentHeader(FS : TFileStream; out Seg : TJFIFSegment):boolean;begin  Result := (FS.Read(Seg,SizeOf(Seg)) = SizeOf(Seg));end; function ReadData(FS : TFileStream; const ALength:WORD; var Data : TByteArr):boolean;begin  SetLength(Data, ALength);  Result := (FS.Read(Data[0],ALength) = ALength);end; function GetJPEGImageSize(const AFileName : UnicodeString; out AHeight, AWidth : dword):boolean;var  FS : TFileStream;  SOI : WORD;  SEG : TJFIFSegment;  SegSize : WORD;  C0 : PSOFData;  tmpData : TByteArr;  UTF8FileName: RawByteString;begin  Result := False;  UTF8FileName := String(AFileName);  FS := TFileStream.Create(UTF8FileName, fmOpenRead or fmShareDenyNone);  try    if ReadWORD(FS, SOI) and (SOI = $FFD8) then begin       While ReadSegmentHeader(FS, SEG) and (SEG.Fix = $FF) do begin         if SEG.Kind = $DA then break;         if ReadWORD(FS, SegSize) then begin          SegSize := SegSize -2;          case SEG.Kind of          $C0, // Baseline DCT            $C1, // Extended sequential DCT, Huffman coding            $C2, // Progressive DCT, Huffman coding          $C3, // Lossless (sequential), Huffman coding            $C9, // Extended sequential DCT, arithmetic coding            $CA, // Progressive DCT, arithmetic coding            $CB : // Lossless (sequential), arithmetic coding                  begin                  if ReadData(FS, SegSize, tmpData) then begin                      C0 := PSOFData(@tmpData[0]);                      AHeight := ReverseWord(C0^.Height);                      AWidth := ReverseWord(C0^.Width);                      Result := True;                    Break;                    end;                  end;         else             FS.Position := FS.Position + SegSize;          end;        end;      end;    end;  finally    FS.Free;  end;end; ---------------------------------------------------------------------- zxandris · 2024-03-07 11:57 You guy's are the best, thank you very much. CJ ---------------------------------------------------------------------- wp · 2024-03-07 15:16 Want to add my usual "advertisement" for fcl-image, the fcl graphics library which comes with FPC by default. The basic image reader type, TFPCustomImageReader, is equipped with a class function "ImageSize(ASize:TPoint)" which determines the image size in pixels without decoding the image data. In case of a jpeg image, the reader class is TFPReaderJPEG in unit fpreadjpeg; therefore, assuming that the image file is accessed via a stream, the image size can be obtained by a one-liner: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Size := TFPReaderJPEG.GetSize(stream); If you have other image file formats you must pick the correct reader class from the fcl-image units. To help you, the basic fcl-image class, TFPCustomImage, has class methods "FindReaderFromStream(AStream)" which determines the required reader class from the file header or "FindReaderFromFileName(AFileName)" which determines the reader class from the extension of the filename. Putting these class methods together you can determine the image size for any image format supported by fcl-image by the following short function (do not forget to list the reader units in the uses clause for all file formats requested): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  [...] fpImage, fpReadJpeg, fpReadBMP, fpReadPNG, fpReadGIF, fpReadPCX, fpReadTIFF, fpReadTGA; function GetImageSize(AFileName: String; out ASize: TPoint): Boolean;var  stream: TStream;  readerClass: TFPCustomImageReaderClass;begin  Result := false;  ASize := Point(-1,-1);  stream := TFileStream.Create(AFileName, fmOpenRead);  try    readerClass := TFPCustomImage.FindReaderFromStream(stream);    if readerClass <> nil then    begin      ASize := readerClass.ImageSize(stream);      Result := true;    end;  finally  stream.Free;  end;end; [EDIT] Just noticed that the helper function InternalSize which is called by the reader to determine the image size has not been implemented for the BMP format - I'll post a bug report (-- done: https://gitlab.com/freepascal.org/fpc/source/-/issues/40685). ---------------------------------------------------------------------- zxandris · 2024-03-08 01:11 Quote from: wp on March 07, 2024, 04:16:34 pmWant to add my usual "advertisement" for fcl-image, the fcl graphics library which comes with FPC by default. The basic image reader type, TFPCustomImageReader, is equipped with a class function "ImageSize(ASize:TPoint)" which determines the image size in pixels without decoding the image data. In case of a jpeg image, the reader class is TFPReaderJPEG in unit fpreadjpeg; therefore, assuming that the image file is accessed via a stream, the image size can be obtained by a one-liner: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Size := TFPReaderJPEG.GetSize(stream); If you have other image file formats you must pick the correct reader class from the fcl-image units. To help you, the basic fcl-image class, TFPCustomImage, has class methods "FindReaderFromStream(AStream)" which determines the required reader class from the file header or "FindReaderFromFileName(AFileName)" which determines the reader class from the extension of the filename. Putting these class methods together you can determine the image size for any image format supported by fcl-image by the following short function (do not forget to list the reader units in the uses clause for all file formats requested): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  [...] fpImage, fpReadJpeg, fpReadBMP, fpReadPNG, fpReadGIF, fpReadPCX, fpReadTIFF, fpReadTGA; function GetImageSize(AFileName: String; out ASize: TPoint): Boolean;var  stream: TStream;  readerClass: TFPCustomImageReaderClass;begin  Result := false;  ASize := Point(-1,-1);  stream := TFileStream.Create(AFileName, fmOpenRead);  try    readerClass := TFPCustomImage.FindReaderFromStream(stream);    if readerClass <> nil then    begin      ASize := readerClass.ImageSize(stream);      Result := true;    end;  finally  stream.Free;  end;end; [EDIT] Just noticed that the helper function InternalSize which is called by the reader to determine the image size has not been implemented for the BMP format - I'll post a bug report (-- done: https://gitlab.com/freepascal.org/fpc/source/-/issues/40685). Just as a matter of interest and bearing in mind I'm unintiated to this library of anything much .  Is this a really fast way of reading those sizes.  Because I have something that I need to read such properties on a fair few files at times, usually jpeg but potentially other types, and need something that willl be fast as possible.  Currently my way of doing that is really slow, as it actually has to load the image and read the width, height that way.  I'm hoping this will be much faster? Thanks, CJ ---------------------------------------------------------------------- Bart · 2024-03-08 09:10 Well, look at the code. It reads just a few bytes from the (file)stream tot detect dimensions, then it returns. Bart ---------------------------------------------------------------------- AlexTP · 2024-03-08 09:34 @wp, I posted your good answer to the wiki: https://wiki.freepascal.org/fcl- image#Getting_image_size_without_loading_the_image_itself Thanks... ---------------------------------------------------------------------- wp · 2024-03-08 10:02 Find a speed test in the attachment, you can compare all methods presented here with a dummy test (just creating the stream and reading the 1st byte) and with full reading of the image. It is hard to tell which method is the "fastest", and in fact it does not matter, because all of them are hundred or thousand times faster than full image reading. I have a folder with 760 relatively small jpeg images (1.5 Mpx). Using the "full images" method it takes 18 seconds to get the image size, while any of the methods presented here does it within a few tens of milliseconds. Another folder with 163 larger images (5 Mpx) is read in 46 s vs about 0.01 - 0.02 s (there is large scatter even in repeated measurements of the fast methods).