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

Import data from video file #334

Merged
merged 3 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/image/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,22 @@ dwv.image.RescaleSlopeAndIntercept.prototype.isID = function () {
* @constructor
* @param {Object} geometry The geometry of the image.
* @param {Array} buffer The image data as an array of frame buffers.
* @param {Number} numberOfFrames The number of frames (optional, can be used
to anticipate the final number after appends).
*/
dwv.image.Image = function(geometry, buffer)
dwv.image.Image = function(geometry, buffer, numberOfFrames)
{
// use buffer length in not specified
if (typeof numberOfFrames === "undefined") {
numberOfFrames = buffer.length;
}

/**
* Get the number of frames.
* @returns {Number} The number of frames.
*/
this.getNumberOfFrames = function () {
return buffer.length;
return numberOfFrames;
};

/**
Expand Down Expand Up @@ -367,6 +374,15 @@ dwv.image.Image = function(geometry, buffer)
return newSliceNb;
};

/**
* Append a frame buffer to the image.
* @param {Object} frameBuffer The frame buffer to append.
*/
this.appendFrameBuffer = function (frameBuffer)
{
buffer.push(frameBuffer);
};

/**
* Get the data range.
* @return {Object} The data range.
Expand Down
144 changes: 122 additions & 22 deletions src/image/reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ var dwv = dwv || {};
dwv.image = dwv.image || {};

/**
* Get data from an input context imageData.
* @param {Object} imageData The context imageData.
* @param {Number} width The width of the coresponding image.
* @param {Number} height The height of the coresponding image.
* @param {Number} sliceIndex The slice index of the imageData.
* @return {Object} The corresponding view.
* Create a simple array buffer from an ImageData buffer.
* @param {Object} imageData The ImageData taken from a context.
* @return {Array} The image buffer.
*/
dwv.image.getViewFromImageData = function (imageData, width, height, sliceIndex) {
dwv.image.imageDataToBuffer = function (imageData) {
// remove alpha
// TODO support passing the full image data
var buffer = [];
Expand All @@ -21,15 +18,31 @@ dwv.image.getViewFromImageData = function (imageData, width, height, sliceIndex)
buffer[j+2] = imageData.data[i+2];
j+=3;
}
// create dwv Image
var imageSize = new dwv.image.Size(width, height);
return buffer;
};

// TODO: wrong info...
/**
* Get data from an input context imageData.
* @param {Number} width The width of the coresponding image.
* @param {Number} height The height of the coresponding image.
* @param {Number} sliceIndex The slice index of the imageData.
* @param {Object} imageBuffer The image buffer.
* @param {Number} numberOfFrames The final number of frames.
* @return {Object} The corresponding view.
*/
dwv.image.getDefaultView = function (
width, height, sliceIndex,
imageBuffer, numberOfFrames) {
// image size
var imageSize = new dwv.image.Size(width, height);
// default spacing
// TODO: misleading...
var imageSpacing = new dwv.image.Spacing(1,1);

// default origin
var origin = new dwv.math.Point3D(0,0,sliceIndex);
// create image
var geometry = new dwv.image.Geometry(origin, imageSize, imageSpacing );
var image = new dwv.image.Image( geometry, [buffer] );
var image = new dwv.image.Image( geometry, imageBuffer, numberOfFrames );
image.setPhotometricInterpretation("RGB");
// meta information
var meta = {};
Expand All @@ -50,6 +63,7 @@ dwv.image.getViewFromImageData = function (imageData, width, height, sliceIndex)
*/
dwv.image.getViewFromDOMImage = function (image)
{
// image size
var width = image.width;
var height = image.height;

Expand All @@ -61,24 +75,110 @@ dwv.image.getViewFromDOMImage = function (image)
ctx.drawImage(image, 0, 0);
// get the image data
var imageData = ctx.getImageData(0, 0, width, height);
// view

// create view
var sliceIndex = image.index ? image.index : 0;
var view = dwv.image.getViewFromImageData(
imageData, width, height, sliceIndex);
// properties
var info = {};
var imageBuffer = dwv.image.imageDataToBuffer(imageData);
var view = dwv.image.getDefaultView(
width, height, sliceIndex, [imageBuffer]);

// image properties
var info = [];
if( image.file )
{
info.fileName = { "value": image.file.name };
info.fileType = { "value": image.file.type };
info.fileLastModifiedDate = { "value": image.file.lastModifiedDate };
info.push({ "name": "fileName", "value": image.file.name });
info.push({ "name": "fileType", "value": image.file.type });
info.push({ "name": "fileLastModifiedDate", "value": image.file.lastModifiedDate });
}
info.imageWidth = { "value": width };
info.imageHeight = { "value": height };
info.push({ "name": "imageWidth", "value": width });
info.push({ "name": "imageHeight", "value": height });

// return
return {"view": view, "info": info};
};

/**
* Get data from an input image using a canvas.
* @param {Object} video The DOM Video.
* @param {Object} callback The function to call once the data is loaded.
*/
dwv.image.getViewFromDOMVideo = function (video, callback)
{
// video size
var width = video.videoWidth;
var height = video.videoHeight;

// default frame rate...
var frameRate = 30;
// number of frames
var numberOfFrames = Math.floor(video.duration * frameRate);

// video properties
var info = [];
if( video.file )
{
info.push({ "name": "fileName", "value": video.file.name });
info.push({ "name": "fileType", "value": video.file.type });
info.push({ "name": "fileLastModifiedDate", "value": video.file.lastModifiedDate });
}
info.push({ "name": "imageWidth", "value": width });
info.push({ "name": "imageHeight", "value": height });
info.push({ "name": "numberOfFrames", "value": numberOfFrames });

// draw the image in the canvas in order to get its data
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');

// using seeked to loop through all video frames
video.addEventListener('seeked', onseeked, false);

// current frame index
var frameIndex = 0;
// video view
var view = null;

// draw the context and store it as a frame
function storeFrame() {
console.log("frame #" + frameIndex);
// draw image
ctx.drawImage(video, 0, 0);
// context to image buffer
var imgBuffer = dwv.image.imageDataToBuffer(
ctx.getImageData(0, 0, width, height) );
if (frameIndex === 0) {
// create view
view = dwv.image.getDefaultView(
width, height, 1, [imgBuffer], numberOfFrames);
// call callback
callback( {"view": view, "info": info } );
} else {
view.appendFrameBuffer(imgBuffer);
}
}

// handle seeked event
function onseeked() {
// store
storeFrame();
// increment index
++frameIndex;
// set the next time
// (not using currentTime, it seems to get offseted)
var nextTime = frameIndex / frameRate;
if (nextTime <= this.duration) {
this.currentTime = nextTime;
} else {
// stop listening
video.removeEventListener('seeked', onseeked);
}
}

// trigger the first seeked
video.currentTime = 0;
};

/**
* Create a dwv.image.View from a DICOM buffer.
* @constructor
Expand Down
9 changes: 9 additions & 0 deletions src/image/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,15 @@ dwv.image.View = function (image)
this.addWindowPresets( rhs.getWindowPresets(), newSliceNumber );
};

/**
* Append a frame buffer to the included image.
* @param {Object} frameBuffer The frame buffer to append.
*/
this.appendFrameBuffer = function (frameBuffer)
{
this.getImage().appendFrameBuffer(frameBuffer);
};

/**
* Set the view window/level.
* @param {Number} center The window center.
Expand Down
48 changes: 40 additions & 8 deletions src/io/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,6 @@ dwv.io.File.prototype.load = function (ioArray)
// set the number of data to load
this.setNToLoad( ioArray.length );

// call the onload listener
var onLoadView = function (data)
{
self.onload(data);
};

// DICOM buffer to dwv.image.View (asynchronous)
var db2v = new dwv.image.DicomBufferToView();
db2v.setDefaultCharacterSet(this.getDefaultCharacterSet());
Expand All @@ -239,7 +233,9 @@ dwv.io.File.prototype.load = function (ioArray)
{
self.setNeedDecoding(true);
try {
db2v.convert(event.target.result, onLoadView);
db2v.convert(event.target.result, function (data) {
self.onload(data);
});
} catch (error) {
self.onerror(error);
}
Expand All @@ -249,7 +245,21 @@ dwv.io.File.prototype.load = function (ioArray)
var onLoadDOMImageBuffer = function (/*event*/)
{
try {
onLoadView( dwv.image.getViewFromDOMImage(this) );
self.onload( dwv.image.getViewFromDOMImage(this) );
self.addLoaded();
} catch (error) {
self.onerror(error);
}
};

// DOM Video buffer to dwv.image.View
var onLoadDOMVideoBuffer = function (/*event*/)
{
try {
dwv.image.getViewFromDOMVideo(this, function (data) {
self.onload(data);
self.addLoaded();
});
} catch (error) {
self.onerror(error);
}
Expand Down Expand Up @@ -277,6 +287,18 @@ dwv.io.File.prototype.load = function (ioArray)
theImage.onload = onLoadDOMImageBuffer;
};

// raw video to DOM Image
var onLoadRawVideoBuffer = function (event)
{
var video = document.createElement('video');
video.src = event.target.result;
// storing values to pass them on
video.file = this.file;
video.index = this.index;
// triggered by ctx.drawImage
video.onloadedmetadata = onLoadDOMVideoBuffer;
};

// loop on I/O elements
for (var i = 0; i < ioArray.length; ++i)
{
Expand All @@ -299,6 +321,16 @@ dwv.io.File.prototype.load = function (ioArray)
reader.onerror = dwv.io.File.createErrorHandler(file, "image", self.onerror);
reader.readAsDataURL(file);
}
else if ( file.type.match("video.*") )
{
// storing values to pass them on
reader.file = file;
reader.index = i;
// callbacks
reader.onload = onLoadRawVideoBuffer;
reader.onerror = dwv.io.File.createErrorHandler(file, "video", self.onerror);
reader.readAsDataURL(file);
}
else
{
reader.onload = onLoadDicomBuffer;
Expand Down
12 changes: 4 additions & 8 deletions src/io/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,6 @@ dwv.io.Url.prototype.load = function (ioArray, requestHeaders)
// set the number of data to load
this.setNToLoad( ioArray.length );

// call the listeners
var onLoadView = function (data)
{
self.onload(data);
};

// DICOM buffer to dwv.image.View (asynchronous)
var db2v = new dwv.image.DicomBufferToView();
db2v.setDefaultCharacterSet(this.getDefaultCharacterSet());
Expand All @@ -239,7 +233,9 @@ dwv.io.Url.prototype.load = function (ioArray, requestHeaders)
{
self.setNeedDecoding(true);
try {
db2v.convert(response, onLoadView);
db2v.convert(response, function (data) {
self.onload(data);
});
} catch (error) {
self.onerror(error);
}
Expand All @@ -249,7 +245,7 @@ dwv.io.Url.prototype.load = function (ioArray, requestHeaders)
var onLoadDOMImageBuffer = function (/*event*/)
{
try {
onLoadView( dwv.image.getViewFromDOMImage(this) );
self.onload( dwv.image.getViewFromDOMImage(this) );
} catch (error) {
self.onerror(error);
}
Expand Down