Jump to content

Parse ImageData with JSON


Recommended Posts

Hi

 

I want to parse the content of a TW3ImageData with JSON for the use with Web Worker. But something is not working in the JSON/ImageData convertion. I made a little test sample :

 

 

procedure TForm1.W3Button3Click(Sender: TObject); 
var 
  imgdata, imgdata2: TW3ImageData; 
  cont: TW3GraphicContext; 
  canv: TW3Canvas; 
  mText: string; 
begin 
  img := TW3Image.Create(self); 
  img.LoadFromURL('res/red3.png'); 
  img.OnLoad := procedure (Sender: TObject) 
                var 
                  i: integer; 
                begin 
                  if img.Ready then 
                  begin 
                    Cont:=TW3GraphicContext.Create(null); 
                    try 
                      Cont.Allocate(img.Width,img.Height); 
                      canv:=TW3Canvas.Create(Cont); 
                      try 
                        canv.DrawImageF(img.Handle,0,0); 
                        imgdata:=canv.toImageData(); 
                        asm 
                          @mText = JSON.stringify(@imgdata); 
                        end; 
                        //... mText To the Worker and back again ... 
                        imgdata2 :=TW3ImageData.Create; 
                        asm 
                          @imgdata2 = JSON.parse(@mText); 
                        end; 

                        W3PaintBox1 := TW3PaintBox.Create(self); 
                        W3PaintBox1.top:=20; 
                        W3PaintBox1.width:=10; 
                        W3PaintBox1.height:=10; 
                        W3PaintBox1.canvas.putimagedata(imgdata2,0,0); 
                      finally 
                        canv.free; 
                      end; 
                    finally 
                      cont.free; 
                    end; 
                  end; 
                end; 
end; 

 

The stringify'ed version of imgdata looks like this :

 

 

{"ClassType":{"$ClassName":"TW3ImageData","$Parent":{"$ClassName":"TObject","$Parent":null}},"FHandle$1":{"height":3,"width":3,"data":{"0":200,"1":50,"2":0,"3":255,"4":200,"5":50,"6":0,"7":255,"8":200,"9":50,"10":0,"11":255,"12":200,"13":50,"14":0,"15":255,"16":200,"17":50,"18":0,"19":255,"20":200,"21":50,"22":0,"23":255,"24":200,"25":50,"26":0,"27":255,"28":200,"29":50,"30":0,"31":255,"32":200,"33":50,"34":0,"35":255,"length":36}}} 

 

 

Link to post
Share on other sites
  • Administrators

I don't know what's going wrong here. I would just suggest to use toDataUrl to convert image to a URL which can be later loaded with a LoadFromURL call. As an added bonus, this will give you a much smaller representation than JSON.Stringify. (Plus, it works.)

procedure TForm1.W3Button1Click(Sender: TObject); 
begin 
  var img := TW3Image.Create(nil); 

  img.OnLoad := lambda(Sender) 
    var 
      mText: string; 
    begin 
      if TW3Image(Sender).Ready then 
      begin 
        mText := TW3Image(Sender).toDataUrl; 
        // mText travels around ... 
        var img2 := TW3Image.Create(nil); 

        img2.OnLoad := lambda(Sender) 
          if img2.Ready then 
            W3PaintBox1.canvas.putimagedata(img2.toImageData,0,0); 
        end; 

        img2.LoadFromURL(mText); 
      end; 
    end; 
  end; 

  img.LoadFromURL('res/red3.png'); 
end; 

Link to post
Share on other sites

I started out using the base64 solution, but then i found out that the Web Worker have no access the the DOM objects. I need to make some pixel processing in the Worker and the common java-solution for that is using the ImageData class.  I dont know how to decode the base64 string in the Worker without a DOM object ?

 

Im not sure about how the JSON part should work in this case, but could it possibly have something to do with the sms-classes being encoded with the ImageData.data part ? I tried using the handle but doesn seem to work either :

 

 

... 

H:=imgdata.Handle; 
asm 
@mText = JSON.stringify(@H); 
end; 

imgdata2 :=TW3ImageData.Create; 
H2:=imgdata2.Handle; 
asm 
@H2 = JSON.parse(@mText); 
end; 

... 

 

Maybe i should copy the data to a UInt8ClampedArray ?

 

 

Link to post
Share on other sites

I don't completely understand what you are doing, but I see you are wanting to decode Base64.  I found this from the free pascal project:

 

http://www.freepascal.org/docs-html/fcl/base64/index.html

https://sites.google.com/a/gorilla3d.com/fpc-docs/built-in-units/base64/tbase64encodingstream

 

Also, I see this:

 

http://sourceforge.net/projects/base64decoder/ written in pascal.  Here is a direct link to where to download the file:

http://sourceforge.net/projects/base64decoder/files/base64decoder/version%202.0/

 

Kevin

Link to post
Share on other sites

@kdtop3 thanks for your links ! It gives me an idea on how the base64 decoding works, but in respect to performance a javascript written decoder would be preferred.

I want to transfer image data to a worker for imageprocessing and back again, and i try to do that without the use of DOM dependend objects in the worker. My samplecode is only to ensure that the encoding/decoding works.

It would be nice if i could use base64 but im not sure how to do that in the worker without a canvas class ? If i can parse it to a TW3ImageData or some kind of array it would be fine.

Link to post
Share on other sites
  • Administrators

Maybe this will help: System.Encoding.pas.

 

This is RTL unit from the next Smart release and it contains Base64 encoder/decoder.

 

BTW, maybe you should move all image processing out of the web worker. Web worker could prepare raw data (maybe in an array as you have suggested) and then you can paint this data in the DOM document.

Link to post
Share on other sites

I finally came to a solution that works. I tried to compare the encoding speed of System.Encoding.pas with the javascript btoa and found the javascript to be about 6 times faster. So i came up with this :

 

 

 

 

// ---------------------------------- In the Worker thread 

TMyImageData = Record 
width  : integer; 
height : integer; 
data   : JUInt8ClampedArray; 
end; 
function Base64Encode_MyImageData(MyImgData:TMyImageData):string; 
var data,w,h : variant; 
begin 
w:=MyImgData.width; 
h:=MyImgData.height; 
data:=MyImgData.data; 
asm 
var w1 = (@w & 0xFF); 
var w2 = ((@w >> 8) & 0xFF); 
var h1 = (@h & 0xFF); 
var h2 = ((@h >> 8) & 0xFF); 
@Result=btoa(String.fromCharCode(w1)+String.fromCharCode(w2)+ 
String.fromCharCode(h1)+String.fromCharCode(h2)+ 
String.fromCharCode.apply(null, @data)); 
end; 
end; 

  

function Base64DecodeTo_MyImageData(b64:string):TMyImageData; 
var l   : integer; 
begin 
asm 
var bytes=atob(@b64); 
@result.width=bytes.charCodeAt(0)+ (bytes.charCodeAt(1) << 8); 
@result.height=bytes.charCodeAt(2)+ (bytes.charCodeAt(3) << 8); 
end; 
l:=4*result.width*result.height; 
result.data:=new JUInt8ClampedArray(l); 
asm 
for (var i=4;i<bytes.length;i++) { @result.data[i-4]=bytes.charCodeAt(i); }; 
end; 
end; 

// -------------------------------- In the main app 

function Base64DecodeTo_ImageData(b64:string):TW3ImageData; 
var l,w,h,i   : integer; 
Handle : variant; 
cont   : TW3GraphicContext; 
canv   : TW3Canvas; 
imgdata : JUInt8ClampedArray; 
begin 
asm 
var bytes=atob(@b64); 
@w=bytes.charCodeAt(0)+ (bytes.charCodeAt(1) << 8); 
@h=bytes.charCodeAt(2)+ (bytes.charCodeAt(3) << 8); 
end; 
Cont:=TW3GraphicContext.Create(Null); 
Cont.Allocate(w,h); 
Canv:=TW3Canvas.Create(Cont); 
Result:=Canv.toImageData; 
Handle:=Result.Handle; 
l:=4*w*h; 
imgdata:=new JUInt8ClampedArray(l); 
asm 
for (var i=4;i<bytes.length;i++) { @imgdata[i-4]=bytes.charCodeAt(i); }; 
end; 
for i:=0 to l-1 do Handle.data[i]:=imgdata[i];   // Handle.data.set(imgdata); 
Canv.Free; 
Cont.Free; 
end; 

  

function Base64Encode_ImageData(ImgData:TW3ImageData):string; 
var data,w,h : variant; 
begin 
w:=imgdata.Handle.width; 
h:=imgdata.Handle.height; 
data:=imgdata.Handle.data; 
asm 
var w1 = (@w & 0xFF); 
var w2 = ((@w >> 8) & 0xFF); 
var h1 = (@h & 0xFF); 
var h2 = ((@h >> 8) & 0xFF); 
@Result=btoa(String.fromCharCode(w1)+String.fromCharCode(w2)+ 
String.fromCharCode(h1)+String.fromCharCode(h2)+ 
String.fromCharCode.apply(null, @data)); 
end; 
end; 

  

. . . 

Base64Encoded:=Base64Encode_ImageData(imagedata); 
// Post to Worker ------------------------------------ 
MyImageData:=Base64DecodeTo_MyImageData(base64Encoded); 

// Paint something in the Worker ... 
for i:=0 to MyImageData.width-1 do MyImageData.data[4*(i+i*MyImageData.width)+1]:=255; 

Base64Encoded:=Base64Encode_MyImageData(MyImageData); 
// Post back to DOM document ---------------------------------- 
W3PaintBox1.canvas.putimagedata(Base64DecodeTo_ImageData(Base64Encoded),0,0); 
. . . 

 

The Base64DecodeTo_ImageData isn't pretty so i will welcome any feedback on optimization ! ;-) Can't use the Handle.data.set(imgdata) to transfer the array ?

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...