ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 ielib64 V6.0.0.0 errors saving JP2 with large images
 New Topic  Reply to Topic
Author Previous Topic Topic Next Topic  

Nick Brett

United Kingdom
6 Posts

Posted - Aug 26 2021 :  07:04:19  Show Profile  Reply
Our Company has a regular need to save quite large colour line scan images and gray scale CT images. Our customers do not want the storage to be lossy, so to date we've always stored the images using ImageEN to create 16-bit tiff files.

For some customers it would be a huge storage advantage to be able to store non-lossy Jpeg 2000 files, but I've had problems doing this due to unexpected image corruption. I've created two Delphi methods to demonstrate problems I've had with 48-bit RGB colour and 16-bit gray scale storing of JP2 data with ImageEN.

48-bit RGB
The method generates and then attempts to save a 16-bit 5000x60000 RGB image in JP2 format.

procedure ImageEN_JP2_RGB48Test;
var
  Image: TImageEnIO;
  Bitmap: TIEBitmap;
  C, X, Y, Z: Integer;
  Pixels: PWordArray;
  IsError: Boolean;
  Value, MinErrorVal, MaxErrorVal: Word;
  ErrorStartX, ErrorStartY, ErrorStartZ: Integer;
  T: UInt64;
const
  CMaxRows = 60000;
  CMaxColumns = 5000;
  CFileName = 'TestImage.jp2';
  CDllName = 'ielib64.dll';
begin
  IEGlobalSettings.JPEG2000Engine := ieenDLL;

  with Memo1.Lines do
  begin
    Clear;
    Add('Testing TImageEnIO saving of a large 48-bit RGB image to JP2');
    Add(String.Empty);
    Image := TImageEnIO.Create(nil);
    try
      if not TFile.Exists(CDllName) then
      begin
        Add('Place ' + CDllName + ' alongside this executable');
        exit;
      end;
      Add(CDllName + ' file version: ' + GetEXEVersionData(CDllName).FileVersion);
      Add('ImageEnVersion: ' + Image.ImageEnVersion);
      Add(Format('Creating a %d x %d RGB image', [CMaxColumns, CMaxRows]));
      Image.NativePixelFormat := True;
      Bitmap := TIEBitmap.Create(CMaxColumns, CMaxRows, ie48RGB);
      Image.AttachedIEBitmap := Bitmap;
      Image.Params.J2000_ColorSpace := ioJ2000_RGB;
      Image.Params.J2000_Rate := 1;
      Image.Params.J2000_Quality := C100_Percent;
      Image.Params.BitsPerSample := 16;
      Image.Params.SamplesPerPixel := 3;
      Image.Params.Width := CMaxColumns;
      Image.Params.Height := CMaxRows;
      Value := 0;
      for Y := 0 to Image.Params.Height - 1 do
      begin
        Pixels := Image.IEBitmap.ScanLine[Y];
        begin
          for X := 0 to Image.Params.Width - 1 do
          begin
            for Z := 0 to Image.Params.SamplesPerPixel - 1 do
            begin
              Pixels[X * 3 + Z] := Value;
              Inc(Value);
            end;
          end;
        end;
      end;

      Add('Checking for created image metadata');
      C := Count;
      try
        if Image.IEBitmap.PixelFormat <> ie48RGB then
          Add('Unexpected Image.IEBitmap.PixelFormat');
        if Image.IEBitmap.Width <> CMaxColumns then
          Add('Unexpected Image.IEBitmap.Width');
        if Image.IEBitmap.Height <> CMaxRows then
          Add('Unexpected Image.IEBitmap.Height');
        if Image.Params.J2000_ColorSpace <> ioJ2000_RGB then
          Add('Unexpected Image.Params.J2000_ColorSpace');
        if Image.Params.BitsPerSample <> 16 then
          Add('Unexpected Image.Params.BitsPerSample');
        if Image.Params.SamplesPerPixel <> 3 then
          Add('Unexpected Image.Params.SamplesPerPixel');
        if Image.Params.Width <> CMaxColumns then
          Add('Unexpected Image.Params.Width');
        if Image.Params.Height <> CMaxRows then
          Add('Unexpected Image.Params.Height');
      finally
        Add(Format('%d metadata errors were found', [Count - C]));
      end;

      Add('Saving the image to ' + CFileName);
      if TFile.Exists(CFileName) then
        TFile.Delete(CFileName);
      T := GetTickCount64;
      try
        Image.SaveToFile(CFileName);
      finally
        Add(Format('File saving took %f seconds', [(GetTickCount64 - T) / 1000]));
      end;

      Add('Checking for metadata corruption after saving');
      C := Count;
      try
        if Image.IEBitmap.PixelFormat <> ie48RGB then
          Add('Unexpected Image.IEBitmap.PixelFormat');
        if Image.IEBitmap.Width <> CMaxColumns then
          Add('Unexpected Image.IEBitmap.Width');
        if Image.IEBitmap.Height <> CMaxRows then
          Add('Unexpected Image.IEBitmap.Height');
        if Image.Params.J2000_ColorSpace <> ioJ2000_RGB then
          Add('Unexpected Image.Params.J2000_ColorSpace');
        if Image.Params.BitsPerSample <> 16 then
          Add('Unexpected Image.Params.BitsPerSample');
        if Image.Params.SamplesPerPixel <> 3 then
          Add('Unexpected Image.Params.SamplesPerPixel');
        if Image.Params.Width <> CMaxColumns then
          Add('Unexpected Image.Params.Width');
        if Image.Params.Height <> CMaxRows then
          Add('Unexpected Image.Params.Height');
      finally
        Add(Format('%d metadata errors were found', [Count - C]));
      end;
    finally
      Image.Free;
    end;

    Image := TImageEnIO.Create(nil);
    try
      Image.NativePixelFormat := True;
      Add('Loading the image from ' + CFileName);
      T := GetTickCount64;
      try
        Image.LoadFromFile(CFileName);
      finally
        Add(Format('File loading took %f seconds', [(GetTickCount64 - T) / 1000]));
      end;

      Add('Checking the loaded image metadata');
      C := Count;
      try
        if Image.IEBitmap.PixelFormat <> ie48RGB then
          Add('Unexpected Image.IEBitmap.PixelFormat');
        if Image.IEBitmap.Width <> CMaxColumns then
          Add('Unexpected Image.IEBitmap.Width');
        if Image.IEBitmap.Height <> CMaxRows then
          Add('Unexpected Image.IEBitmap.Height');
        if Image.Params.J2000_ColorSpace <> ioJ2000_RGB then
          Add('Unexpected Image.Params.J2000_ColorSpace');
        if Image.Params.BitsPerSample <> 16 then
          Add('Unexpected Image.Params.BitsPerSample');
        if Image.Params.SamplesPerPixel <> 3 then
          Add('Unexpected Image.Params.SamplesPerPixel');
        if Image.Params.Width <> CMaxColumns then
          Add('Unexpected Image.Params.Width');
        if Image.Params.Height <> CMaxRows then
          Add('Unexpected Image.Params.Height');
      finally
        Add(Format('%d metadata errors were found', [Count - C]));
      end;

      Add('Checking the image for pixel errors');
      IsError := False;
      ErrorStartX := 0;
      ErrorStartY := 0;
      ErrorStartZ := 0;
      MinErrorVal := Word.MaxValue;
      MaxErrorVal := Word.MinValue;
      try
        Value := 0;
        for Y := 0 to Image.Params.Height - 1 do
        begin
          Pixels := Image.IEBitmap.ScanLine[Y];
          begin
            for X := 0 to Image.Params.Width - 1 do
            begin
              for Z := 0 to Image.Params.SamplesPerPixel - 1 do
              begin
                if (not IsError) and (Pixels[X * 3 + Z] <> Value) then
                begin
                  ErrorStartX := X;
                  ErrorStartY := Y;
                  ErrorStartZ := Z;
                  MinErrorVal := Word.MaxValue;
                  MaxErrorVal := Word.MinValue;
                  IsError := True;
                end;
                if IsError then
                begin
                  MinErrorVal := Min(MinErrorVal, Pixels[X * 3 + Z]);
                  MaxErrorVal := Max(MaxErrorVal, Pixels[X * 3 + Z]);
                end;
                Inc(Value);
              end;
            end;
          end;
        end;
      finally
        if not IsError then
          Add('There were no image pixel errors')
        else
        begin
          Add(Format('Image pixel errors start at row %d column %d word %d', [ErrorStartY, ErrorStartX, ErrorStartZ]));
          Add(Format('Erroroneous pixel values ranged from %d to %d', [Integer(MinErrorVal), Integer(MaxErrorVal)]));
        end;
      end;
      Add(String.Empty);
      Add('Testing has ended');
    finally
      FreeAndNil(Image);
      FreeAndNil(Bitmap);
    end;
  end;
end;


This code generates the following output:

Testing TImageEnIO saving of a large 48-bit RGB image to JP2

ielib64.dll file version: 6.0.0.0
ImageEnVersion: 10.1.0
Creating a 5000 x 60000 RGB image
Checking for created image metadata
0 metadata errors were found
Saving the image to TestImage.jp2
File saving took 39.74 seconds
Checking for metadata corruption after saving
0 metadata errors were found
Loading the image from TestImage.jp2
File loading took 23.64 seconds
Checking the loaded image metadata
0 metadata errors were found
Checking the image for pixel errors
Image pixel errors start at row 59995 column 0 word 0
Erroroneous pixel values ranged from 0 to 0

Testing has ended


The error is simply that the last few pixel rows are all unexpectedly zero. This doesn't happen if the number of pixel rows is less than 20000, but seems to be related to the size of the image as the same thing happens if the number of columns is made much larger.

16-bit gray scale

The method is very similar to the 48-bit RGB method, generating and then attempting to save a 16-bit 9000x100000 gray scale image in JP2 format. The size of the image is larger because the corruption doesn't happen with a 5000x60000 gray scale image, so maybe the problem is related to the overall image size rather than dimensions.

procedure ImageEN_JP2_G16Test;
var
  Image: TImageEnIO;
  Bitmap: TIEBitmap;
  C, X, Y: Integer;
  Pixels: PWordArray;
  IsError: Boolean;
  Value, MinErrorVal, MaxErrorVal: Word;
  ErrorStartX, ErrorStartY: Integer;
  T: UInt64;
const
  CMaxRows = 100000;
  CMaxColumns = 9000;
  CFileName = 'TestImage.jp2';
  CDllName = 'ielib64.dll';
begin
  IEGlobalSettings.JPEG2000Engine := ieenDLL;

  with Memo1.Lines do
  begin
    Clear;
    Add('Testing TImageEnIO saving of a large 16-bit gray scale image to JP2');
    Add(String.Empty);
    Image := TImageEnIO.Create(nil);
    try
      if not TFile.Exists(CDllName) then
      begin
        Add('Place ' + CDllName + ' alongside this executable');
        exit;
      end;
      Add(CDllName + ' file version: ' + GetEXEVersionData(CDllName).FileVersion);
      Add('ImageEnVersion: ' + Image.ImageEnVersion);
      Add(Format('Creating a %d x %d gray scale image', [CMaxColumns, CMaxRows]));
      Image.NativePixelFormat := True;
      Bitmap := TIEBitmap.Create(CMaxColumns, CMaxRows, ie16G);
      Image.AttachedIEBitmap := Bitmap;
      Image.Params.J2000_ColorSpace := ioJ2000_GRAYLEV;
      Image.Params.J2000_Rate := 1;
      Image.Params.J2000_Quality := C100_Percent;
      Image.Params.BitsPerSample := 16;
      Image.Params.SamplesPerPixel := 1;
      Image.Params.Width := CMaxColumns;
      Image.Params.Height := CMaxRows;
      Value := 0;
      for Y := 0 to Image.Params.Height - 1 do
      begin
        Pixels := Image.IEBitmap.ScanLine[Y];
        for X := 0 to Image.Params.Width - 1 do
        begin
          Pixels[X] := Value;
          Inc(Value);
        end;
      end;

      Add('Checking for created image metadata');
      C := Count;
      try
        if Image.IEBitmap.PixelFormat <> ie16G then
          Add('Unexpected Image.IEBitmap.PixelFormat');
        if Image.IEBitmap.Width <> CMaxColumns then
          Add('Unexpected Image.IEBitmap.Width');
        if Image.IEBitmap.Height <> CMaxRows then
          Add('Unexpected Image.IEBitmap.Height');
        if Image.Params.J2000_ColorSpace <> ioJ2000_GRAYLEV then
          Add('Unexpected Image.Params.J2000_ColorSpace');
        if Image.Params.BitsPerSample <> 16 then
          Add('Unexpected Image.Params.BitsPerSample');
        if Image.Params.SamplesPerPixel <> 1 then
          Add('Unexpected Image.Params.SamplesPerPixel');
        if Image.Params.Width <> CMaxColumns then
          Add('Unexpected Image.Params.Width');
        if Image.Params.Height <> CMaxRows then
          Add('Unexpected Image.Params.Height');
      finally
        Add(Format('%d metadata errors were found', [Count - C]));
      end;

      Add('Saving the image to ' + CFileName);
      if TFile.Exists(CFileName) then
        TFile.Delete(CFileName);
      T := GetTickCount64;
      try
        Image.SaveToFile(CFileName);
      finally
        Add(Format('File saving took %f seconds', [(GetTickCount64 - T) / 1000]));
      end;

      Add('Checking for metadata corruption after saving');
      C := Count;
      try
        if Image.IEBitmap.PixelFormat <> ie16G then
          Add('Unexpected Image.IEBitmap.PixelFormat');
        if Image.IEBitmap.Width <> CMaxColumns then
          Add('Unexpected Image.IEBitmap.Width');
        if Image.IEBitmap.Height <> CMaxRows then
          Add('Unexpected Image.IEBitmap.Height');
        if Image.Params.J2000_ColorSpace <> ioJ2000_GRAYLEV then
          Add('Unexpected Image.Params.J2000_ColorSpace');
        if Image.Params.BitsPerSample <> 16 then
          Add('Unexpected Image.Params.BitsPerSample');
        if Image.Params.SamplesPerPixel <> 1 then
          Add('Unexpected Image.Params.SamplesPerPixel');
        if Image.Params.Width <> CMaxColumns then
          Add('Unexpected Image.Params.Width');
        if Image.Params.Height <> CMaxRows then
          Add('Unexpected Image.Params.Height');
      finally
        Add(Format('%d metadata errors were found', [Count - C]));
      end;
    finally
      Image.Free;
    end;

    Image := TImageEnIO.Create(nil);
    try
      Image.NativePixelFormat := True;
      Add('Loading the image from ' + CFileName);
      T := GetTickCount64;
      try
        Image.LoadFromFile(CFileName);
      finally
        Add(Format('File loading took %f seconds', [(GetTickCount64 - T) / 1000]));
      end;

      Add('Checking the loaded image metadata');
      C := Count;
      try
        if Image.IEBitmap.PixelFormat <> ie16G then
          Add('Unexpected Image.IEBitmap.PixelFormat');
        if Image.IEBitmap.Width <> CMaxColumns then
          Add('Unexpected Image.IEBitmap.Width');
        if Image.IEBitmap.Height <> CMaxRows then
          Add('Unexpected Image.IEBitmap.Height');
        if Image.Params.J2000_ColorSpace <> ioJ2000_GRAYLEV then
          Add('Unexpected Image.Params.J2000_ColorSpace');
        if Image.Params.BitsPerSample <> 16 then
          Add('Unexpected Image.Params.BitsPerSample');
        if Image.Params.SamplesPerPixel <> 1 then
          Add('Unexpected Image.Params.SamplesPerPixel');
        if Image.Params.Width <> CMaxColumns then
          Add('Unexpected Image.Params.Width');
        if Image.Params.Height <> CMaxRows then
          Add('Unexpected Image.Params.Height');
      finally
        Add(Format('%d metadata errors were found', [Count - C]));
      end;

      Add('Checking the image for pixel errors');
      IsError := False;
      ErrorStartX := 0;
      ErrorStartY := 0;
      MinErrorVal := Word.MaxValue;
      MaxErrorVal := Word.MinValue;
      try
        Value := 0;
        for Y := 0 to Image.Params.Height - 1 do
        begin
          Pixels := Image.IEBitmap.ScanLine[Y];
          begin
            for X := 0 to Image.Params.Width - 1 do
            begin
              if (not IsError) and (Pixels[X] <> Value) then
              begin
                ErrorStartX := X;
                ErrorStartY := Y;
                MinErrorVal := Word.MaxValue;
                MaxErrorVal := Word.MinValue;
                IsError := True;
              end;
              if IsError then
              begin
                MinErrorVal := Min(MinErrorVal, Pixels[X]);
                MaxErrorVal := Max(MaxErrorVal, Pixels[X]);
              end;
              Inc(Value);
            end;
          end;
        end;
      finally
        if not IsError then
          Add('There were no image pixel errors')
        else
        begin
          Add(Format('Image pixel errors start at row %d column %d', [ErrorStartY, ErrorStartX]));
          Add(Format('Erroroneous pixel values ranged from %d to %d', [Integer(MinErrorVal), Integer(MaxErrorVal)]));
        end;
      end;
      Add(String.Empty);
      Add('Testing has ended');
    finally
      FreeAndNil(Image);
      FreeAndNil(Bitmap);
    end;
  end;
end;


This code generates the following output:

Testing TImageEnIO saving of a large 16-bit gray scale image to JP2

ielib64.dll file version: 6.0.0.0
ImageEnVersion: 10.1.0
Creating a 9000 x 100000 gray scale image
Checking for created image metadata
0 metadata errors were found
Saving the image to TestImage.jp2
File saving took 64.64 seconds
Checking for metadata corruption after saving
Unexpected Image.IEBitmap.PixelFormat
1 metadata errors were found
Loading the image from TestImage.jp2
File loading took 27.77 seconds
Checking the loaded image metadata
0 metadata errors were found
Checking the image for pixel errors
Image pixel errors start at row 99996 column 0
Erroroneous pixel values ranged from 0 to 0

Testing has ended


Again the main error is that the last few pixel rows are all unexpectedly zero. In addition notice that for gray-scale images the data has been corrupted immediately after the save, as the IEBitmap has been unexpectedly converted to 48-bit RGB format.

We're using the very latest ImageEN version downloaded yesterday, with the latest ielib64.dll library (file version 6.0.0.0 according to its properties in Windows). The 32-bit version of the same dll library appears to generate identical results and problems.

I'm hope you're able to help with fixes very soon.

xequte

38798 Posts

Posted - Aug 26 2021 :  18:31:23  Show Profile  Reply
Thanks for the detail, Nick. We will investigate.



Nigel
Xequte Software
www.imageen.com
Go to Top of Page

xequte

38798 Posts

Posted - Aug 27 2021 :  20:05:13  Show Profile  Reply
Hi Nick

We have fixes available for both of these issues. You can email me for the current beta.

Nigel
Xequte Software
www.imageen.com
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
Jump To: