Forum > Graphics

[SOLVED] Trying to efficiently encode/decode images....

(1/1)

EganSolo:
I want a bit of code that can encode and decode a TImage into a string. I created a simple program with two TImage components, a memo and two buttons: btn_Encode which encodes the content of image1 into a string displayed into the memo and btn_decode which is supposed to take the string in the memo and decode it back into an image to be displayed into Image2 but I've got a bug in that return leg (please see below)

The encoding process, I took from this topic https://stackoverflow.com/questions/57481399/lazarus-timage-to-base64 on Stackoverflow with a minor modification.


--- Quote ---
TBase64EncodingStream is not suitable for big binary conversion, I mean, it will be very slow, also Image1.Picture.SaveToStream, by default, will save the picture as Bitmap format, so it will be very big. Try using JPG format.
--- End quote ---


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.Button1Click(Sender: TObject);var  imgstream, Outputstream: TStream;  Encoder: TBase64EncodingStream;  jpg: TJPEGImage;  I: Int64;begin  I := GetTickCount64;  imgstream := TMemoryStream.Create();  Outputstream := TStringStream.Create('');  jpg := TJPEGImage.Create;  Encoder := TBase64EncodingStream.Create(Outputstream);  try    jpg.Assign(Image1.Picture.Bitmap);    jpg.CompressionQuality:=75;    jpg.SaveToStream(imgstream);    imgstream.Position:= 0;    //the following line throws an exception: wrong typecast.    //But typecast is not required.    //Encoder.CopyFrom(TStringStream(imgstream), imgstream.Size);    Encoder.CopyFrom(imgstream, imstream.size);    Encoder.Flush;    Memo1.Text:='data:image/jpg;base64,'+ TStringStream(Outputstream).DataString;    ShowMessage('Created in '+IntToStr(GetTickCount64-I)+'ms');  finally    imgstream.Free;    Encoder.Free;    Outputstream.Free;    jpg.Free;  end;end;        
This works. So, with my limited knowledge of these formats, I tried to write the reverse code and I'm bombing...

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- Procedure TForm1.btn_decodeClick(Sender: TObject);var  imgstream, Inputstream: TStream;  //<--- this line was cut off during the copy and paste.  Decoder: TBase64DecodingStream;  jpg: TJPEGImage;  I: Int64;begin  I := GetTickCount64;  //First read the memo back into a string stream  InputStream := TStringStream.Create(memo1.Text);  //Point the decoder to that string stream  Decoder     := TBase64DecodingStream.Create(InputStream);  InputStream.Position := 0;  //Next create the jpg image.  jpg         := TJPEGImage.Create; //Not sure this line is required?  jpg.CompressionQuality := 75;  try    //If the decoder is working the right way, then it should decode the string back into    //a jpg image, no? If so, I should be able to load it into the jpg image but I'm missing    //something.    jpg.LoadFromStream(Decoder, Decoder.Size); //<--- This lines bomb: wrong format    //Now back assign the jpg image into a TImage    Image2.Assign(jpg);    ShowMessage('Created in ' + IntToStr(GetTickCount64-i) + 'ms');  Finally    decoder.free;    inputstream.free;    jpg.free;  End;end; 

eljo:

--- Quote from: EganSolo on May 03, 2023, 08:22:49 am ---I want a bit of code that can encode and decode a TImage into a string. I created a simple program with two TImage components, a memo and two buttons: btn_Encode which encodes the content of image1 into a string displayed into the memo and btn_decode which is supposed to take the string in the memo and decode it back into an image to be displayed into Image2 but I've got a bug in that return leg (please see below)

The encoding process, I took from this topic https://stackoverflow.com/questions/57481399/lazarus-timage-to-base64 on Stackoverflow with a minor modification.


--- Quote ---
TBase64EncodingStream is not suitable for big binary conversion, I mean, it will be very slow, also Image1.Picture.SaveToStream, by default, will save the picture as Bitmap format, so it will be very big. Try using JPG format.
--- End quote ---


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.Button1Click(Sender: TObject);var  imgstream, Outputstream: TStream;  Encoder: TBase64EncodingStream;  jpg: TJPEGImage;  I: Int64;begin  I := GetTickCount64;  imgstream := TMemoryStream.Create();  Outputstream := TStringStream.Create('');  jpg := TJPEGImage.Create;  Encoder := TBase64EncodingStream.Create(Outputstream);  try    jpg.Assign(Image1.Picture.Bitmap);    jpg.CompressionQuality:=75;    jpg.SaveToStream(imgstream);    imgstream.Position:= 0;    //the following line throws an exception: wrong typecast.    //But typecast is not required.    //Encoder.CopyFrom(TStringStream(imgstream), imgstream.Size);    Encoder.CopyFrom(imgstream, imstream.size);    Encoder.Flush;    Memo1.Text:='data:image/jpg;base64,'+ TStringStream(Outputstream).DataString;    ShowMessage('Created in '+IntToStr(GetTickCount64-I)+'ms');  finally    imgstream.Free;    Encoder.Free;    Outputstream.Free;    jpg.Free;  end;end;        
This works. So, with my limited knowledge of these formats, I tried to write the reverse code and I'm bombing...

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- Procedure TForm1.btn_decodeClick(Sender: TObject);var  Decoder: TBase64DecodingStream;  jpg: TJPEGImage;  I: Int64;begin  I := GetTickCount64;  //First read the memo back into a string stream  InputStream := TStringStream.Create(memo1.Text);  //Point the decoder to that string stream  Decoder     := TBase64DecodingStream.Create(InputStream);  InputStream.Position := 0;  //Next create the jpg image.  jpg         := TJPEGImage.Create; //Not sure this line is required?  jpg.CompressionQuality := 75;  try    //If the decoder is working the right way, then it should decode the string back into    //a jpg image, no? If so, I should be able to load it into the jpg image but I'm missing    //something.    jpg.LoadFromStream(Decoder, Decoder.Size); //<--- This lines bomb: wrong format    //Now back assign the jpg image into a TImage    Image2.Assign(jpg);    ShowMessage('Created in ' + IntToStr(GetTickCount64-i) + 'ms');  Finally    decoder.free;    inputstream.free;    jpg.free;  End;end; 
--- End quote ---

Please provide an accurate copy of your code, on the code provided the InputStream is not defined in the decode click it should bomb a lot earlier unless there is a global variable inputstream somewhere else in your code. Better yet zip a project that demonstrates the problem and attach it to a message.

paweld:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.Button1Click(Sender: TObject);var  InputStream, OutputStream: TStringStream;  Encoder: TBase64EncodingStream;  jpg: TJPEGImage;  I: Int64;begin  I := GetTickCount64;  InputStream := TStringStream.Create;  OutputStream := TStringStream.Create;  jpg := TJPEGImage.Create;  Encoder := TBase64EncodingStream.Create(OutputStream);  try    jpg.Assign(Image1.Picture.Bitmap);    jpg.CompressionQuality := 75;    jpg.SaveToStream(InputStream);    Encoder.CopyFrom(InputStream, 0);    Encoder.Flush;    Memo1.Lines.Text := 'data:image/jpg;base64,' + OutputStream.DataString;    ShowMessage('Created in ' + IntToStr(GetTickCount64 - I) + 'ms');  finally    InputStream.Free;      OutputStream.Free;    Encoder.Free;    jpg.Free;  end;end; procedure TForm1.Button2Click(Sender: TObject);var  InputStream, OutputStream: TStringStream;  s: String;  Decoder: TBase64DecodingStream;  jpg: TJPEGImage;  I: Int64;begin  I := GetTickCount64;  //First read the memo back into a string stream  //with remove mime type info  s := Memo1.Lines.Text;  if pos(',', s) > 1 then    Delete(s, 1, pos(',', s));  InputStream := TStringStream.Create(s);    InputStream.Position := 0;  OutputStream := TStringStream.Create;  //Point the Decoder to that string stream  Decoder := TBase64DecodingStream.Create(InputStream);  //Next create the jpg image.  jpg := TJPEGImage.Create;  try    OutputStream.CopyFrom(Decoder, Decoder.Size);    OutputStream.Position := 0;    jpg.LoadFromStream(OutputStream);    //Now back assign the jpg image into a TImage    Image2.Picture.Assign(jpg);    ShowMessage('Created in ' + IntToStr(GetTickCount64 - i) + 'ms');  finally                  InputStream.Free;     OutputStream.Free;    Decoder.Free;    jpg.Free;  end;end;     

marcov:
jpg runs compression, which is probably slower than encoding as string.

But possibly the slowest is putting very large blocks of text in a memo. Benchmark before the memo.

Navigation

[0] Message Index

Go to full version