Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TinyEXR JavaScript version in a browser #43

Closed
Kif11 opened this issue Sep 26, 2017 · 19 comments
Closed

TinyEXR JavaScript version in a browser #43

Kif11 opened this issue Sep 26, 2017 · 19 comments

Comments

@Kif11
Copy link
Contributor

Kif11 commented Sep 26, 2017

I've been trying to run JavaScript version provided in this repo in a browser, but I'm stuck on the step of converting image blob to a nodejs like Buffer. Please suggest

Here is my code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>TinyEXR Js</title>
  <script src="tinyexr_new.js"></script>
</head>
<body>
</body>
<script>

  var imgData;
  fetch("asakusa.exr")
  .then(function (response) {
    return response.blob();
  })
  .then(function (blob) {

    // TODO(Kirill):
    // Need to convert image blob to a nodejs like Buffer

    // here the image is a blob
    var data = new Uint8Array(buffer);
    console.log('Data length:', data.length);

    ParseEXRHeaderFromMemory = cwrap(
      'ParseEXRHeaderFromMemory', 'number', ['number', 'number', 'number']
    );

    LoadEXRFromMemory = cwrap(
      'LoadEXRFromMemory', 'number', ['number', 'number', 'string']
    );

    var widthPtr = _malloc(4);
    var widthHeap = new Uint8Array(HEAPU8.buffer, widthPtr, 4);
    var heightPtr = _malloc(4);
    var heightHeap = new Uint8Array(HEAPU8.buffer, heightPtr, 4);
    var ptr = _malloc(data.length)
    var dataHeap = new Uint8Array(HEAPU8.buffer, ptr, data.length);
    dataHeap.set(new Uint8Array(data.buffer))

    var ret  = ParseEXRHeaderFromMemory(widthHeap.byteOffset, heightHeap.byteOffset, dataHeap.byteOffset);
    console.log('Exr header:', ret);

    var width = (new Int32Array(widthHeap.buffer, widthHeap.byteOffset, 1))[0];
    var height = (new Int32Array(heightHeap.buffer, heightHeap.byteOffset, 1))[0];
    console.log('Width and height: ', width, height)

    var imgDataLen = width * height * 4 * 4;
    var img = _malloc(imgDataLen)
    var imgHeap = new Float32Array(HEAPU8.buffer, img, imgDataLen/4);
    ret = LoadEXRFromMemory(imgHeap.byteOffset, dataHeap.byteOffset, null)

    // Now imgHeap contains HDR image: float x RGBA x width x height
    console.log(imgHeap[0])
  });



</script>
</html>
@syoyo
Copy link
Owner

syoyo commented Sep 26, 2017

TinyEXR JS is experimental and not maintained, so will not work well ATM. Related: #25

I will try to regenerate .js using recent emscripten.
https://github.com/syoyo/tinyexr/blob/master/compile_to_js.sh

FYI, em++ on Ubuntu 16.04 results in the following error:

Exception in thread Thread-6:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 389, in _handle_results
    task = get()
TypeError: ('__init__() takes at least 3 arguments (1 given)', <class 'subprocess.CalledProcessError'>, ())

@Kif11
Copy link
Contributor Author

Kif11 commented Oct 9, 2017

So, I manage to load an exr in a browser with tinyexr.js that shipped with this repo. However, when I compile it myself with Emscripten 1.37.21 on OSX 10.12.6 it give me -4 return code for ParseEXRHeaderFromMemory. I'm using compile_to_js.sh script.

I'm planing to setup a working demo but first I need to figure out why my build is not working :(

@syoyo
Copy link
Owner

syoyo commented Oct 10, 2017

API has been changed thus you need to rewrite JS code for reading EXR based on current API signature.
(tinyexr.js included in the repo is too old and has different API signature)

Also, you could directly use LoadEXRFromMemory thanks to this PR: #44

@Kif11
Copy link
Contributor Author

Kif11 commented Oct 15, 2017

Hi @syoyo, can you please help us to figure it out. We've been trying to make it work with the new API but we can make it work. Our current code looks something like this:

var fs = require('fs');
var tinyexr = require('../../tinyexr')

var data = new Uint8Array(fs.readFileSync("../../asakusa.exr"))

FromMemory = tinyexr.cwrap(
  'LoadEXRFromMemory', 'number', ['number', 'number', 'number', 'number', 'number', 'string']
);

var widthPtr = tinyexr._malloc(4);
var widthHeap = new Uint8Array(tinyexr.HEAPU8.buffer, widthPtr, 4);
var heightPtr = tinyexr._malloc(4);
var heightHeap = new Uint8Array(tinyexr.HEAPU8.buffer, heightPtr, 4);
var ptr = tinyexr._malloc(data.length)
var dataHeap = new Uint8Array(tinyexr.HEAPU8.buffer, ptr, data.length);
dataHeap.set(new Uint8Array(data.buffer))
dataHeap.byteOffset);

let width = 660;
let height = 440;
let filesize = 1127425;

var imgDataLen = width * height * 4 * 4;
var img = tinyexr._malloc(imgDataLen)
var imgHeap = new Float32Array(tinyexr.HEAPU8.buffer, img, imgDataLen/4);

ret = LoadEXRFromMemory(imgHeap.byteOffset, widthHeap.byteOffset, heightHeap.byteOffset, dataHeap.byteOffset, filesize, null);

@syoyo
Copy link
Owner

syoyo commented Oct 16, 2017

var imgHeap = new Float32Array(tinyexr.HEAPU8.buffer, img, imgDataLen/4);
ret = LoadEXRFromMemory(imgHeap.byteOffset, widthHeap.byteOffset, heightHeap.byteOffset, dataHeap.byteOffset, filesize, null);

LoadEXRFromMemory now allocates buffer for output image, thus you don't need to pre allocate buffer.

But I'm not sure how to handle such a function signature(float **) in JavaScript/Emscripten layer.

@syoyo
Copy link
Owner

syoyo commented Oct 16, 2017

It looks it is difficult to manage complex C function signature in pure JS layer, thus I am considering to write JS bindings in C++ layer.

emscripten-core/emscripten#3083

@sneha-belkhale
Copy link

Hey @syoyo ,

I have been working with @Kif11 on trying to integrate the new tinyexr API with javascript.

So far, I have found a way to use the new API, but it requires a few adjustments such as changing out_rgba from float ** to float * in LoadEXRFromMemory. It also requires pre-allocating the output image buffer.

int LoadEXRFromMemory(float *out_rgba, int *width, int *height,
	const unsigned char *memory, size_t size,
	const char **err) {
  if (out_rgba == NULL || memory == NULL) {
    if (err) {
      (*err) = "Invalid argument.\n";
    }
    return TINYEXR_ERROR_INVALID_ARGUMENT;
  }
  //---- break --- //
  // out_rgba = reinterpret_cast<float *>(
	// malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
	//   static_cast<size_t>(exr_image.height)));

  for (int i = 0; i < exr_image.width * exr_image.height; i++) {
	out_rgba[4 * i + 0] =
	 reinterpret_cast<float **>(exr_image.images)[idxR][i];
	out_rgba[4 * i + 1] =
	 reinterpret_cast<float **>(exr_image.images)[idxG][i];
	out_rgba[4 * i + 2] =
	 reinterpret_cast<float **>(exr_image.images)[idxB][i];
	if (idxA != -1) {
	 out_rgba[4 * i + 3] =
	  reinterpret_cast<float **>(exr_image.images)[idxA][i];
	}
	else {
	 out_rgba[4 * i + 3] = 1.0;
	}
  }

  (*width) = exr_image.width;
  (*height) = exr_image.height;

  FreeEXRHeader(&exr_header);
  FreeEXRImage(&exr_image);

  return TINYEXR_SUCCESS;
}

Is there a reason why you use double pointers and allocate memory inside LoadEXRFromMemory ?

Thanks,

Sneha

@sneha-belkhale
Copy link

For reference, here is the javascript side.

var fs = require('fs');
var tinyexr = require('../../tinyexr')

var data = new Uint8Array(fs.readFileSync("../../asakusa.exr"))

LoadEXRFromMemory = tinyexr.cwrap(
  'LoadEXRFromMemory', 'number', ['number', 'number', 'number', 'number', 'number', 'string']
);

var widthPtr = tinyexr._malloc(4);
var widthHeap = new Uint8Array(tinyexr.HEAPU8.buffer, widthPtr, 4);
var heightPtr = tinyexr._malloc(4);
var heightHeap = new Uint8Array(tinyexr.HEAPU8.buffer, heightPtr, 4);
var ptr = tinyexr._malloc(data.length)
var dataHeap = new Uint8Array(tinyexr.HEAPU8.buffer, ptr, data.length);
dataHeap.set(new Uint8Array(data.buffer))

let width = 660;
let height = 440;

var imgDataLen = width * height * 4 * 4;
var img = tinyexr._malloc(imgDataLen)
var imgHeap = new Float32Array(tinyexr.HEAPU8.buffer, img, imgDataLen/4);

ret = LoadEXRFromMemory(imgHeap.byteOffset, widthHeap.byteOffset, heightHeap.byteOffset, dataHeap.byteOffset, 1127425, null);

console.log('LoadEXRFromMemory return: ', ret);
// Now imgHeap contains HDR image: float x RGBA x width x height

@syoyo
Copy link
Owner

syoyo commented Oct 20, 2017

I am working on embind based implementation in emscripten branch.

https://github.com/syoyo/tinyexr/tree/emscripten/experimental/js

And its near to get it work(probably by this weekend(22 Oct, 2017).

Is there a reason why you use double pointers and allocate memory inside LoadEXRFromMemory ?

This is for matching similar behavior with LoadEXR #44

@syoyo
Copy link
Owner

syoyo commented Oct 20, 2017

I have finished initial embind-based implementation of TinyEXR. Apparently it works. Could you please write HTML5 Canvas example JS code so that the binding works well or not, @Kif11 @sneha-belkhale ?

https://github.com/syoyo/tinyexr/tree/emscripten/experimental/js

(FYI, regenerated tinyexr.js using compile_to_js.sh is added to the repo)

@Kif11
Copy link
Contributor Author

Kif11 commented Oct 20, 2017

@syoyo looks like the embind is still broken. The first pixel of the data image array is correct but the rest are zeroes.

$: node test.js 
1127425
true
660
440
Float32Array [
  0.84716796875,
  0.92529296875,
  0.9609375,
  1,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0 ...

I have a HTML canvas example ready. I just need a working embind...

@syoyo
Copy link
Owner

syoyo commented Oct 21, 2017

Now fixed in this commit: 2209ea4

I have a HTML canvas example ready.

Awesome!

@syoyo
Copy link
Owner

syoyo commented Oct 21, 2017

I have merged @Kif11's HTMT5 canvas example, and also merged emscripten branch into master. I think this issue is now ready to close.

@Kif11
Copy link
Contributor Author

Kif11 commented Oct 21, 2017

I think so too. Closing...

@Kif11 Kif11 closed this as completed Oct 21, 2017
@syoyo
Copy link
Owner

syoyo commented Oct 21, 2017

Thanks!

@syoyo syoyo mentioned this issue Oct 21, 2017
@stephane-lb
Copy link

stephane-lb commented Oct 23, 2017

Hello,

Thanks @syoyo to ping me in #25 .

Just tested it and it is loading something but it is looks wrong when displayed.

Here the exrinfo of the image I am loading :

file format version: 2, flags 0x0
channels (type chlist):
    B, 32-bit floating-point, sampling 1 1
    G, 32-bit floating-point, sampling 1 1
    R, 32-bit floating-point, sampling 1 1
compression (type compression): none
dataWindow (type box2i): (0 0) - (511 511)
displayWindow (type box2i): (0 0) - (511 511)
lineOrder (type lineOrder): increasing y
pixelAspectRatio (type float): 1
screenWindowCenter (type v2f): (0 0)
screenWindowWidth (type float): 512
type (type string): "scanlineimage"

Did I miss something ? Works well when displaying in it ( Pixar ) ?

Any idea ?

Many thanks,

Stephane

@syoyo
Copy link
Owner

syoyo commented Oct 23, 2017

Please attach .exr file to reproduce the issue.

@stephane-lb
Copy link

Sure : https://drive.google.com/open?id=0B4OKayijuI7xRVQyN2lOMlZwVjQ ( Github is blocking exr )

@syoyo
Copy link
Owner

syoyo commented Oct 23, 2017

Thank you for the file.

Hmm..., it looks LoadEXRFromMemory cannot handle well for non-compressed, fp32 EXR image.

Reading your red-fabric.exr with test.js shows somewhat invalid pixel values.

3154233
true
512
512
-2.462492147969897e-7
-2.11823082847978e-32
8.83701432030648e-8
1
-0.00002982292244269047
-1.1201922672890615e-23
157956.9375
1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants