Major Update for LEADTOOLS 17.5 Released!

We’ve released a new major update for LEADTOOLS 17.5 which includes the new HTML5 features I teased you with earlier, as well as many updates to the Document, Medical, Imaging and Vector engines.

This update is highlighted by new HTML5 viewer features, PDF annotations, speed and accuracy boosts to OCR, new formats and way too much more to summarize here. Check out the official press release and list of what’s new. I hope you will be as excited about this update as we are!

Thanks,
Otis Goodwin

Posted in General | Tagged , , , , , , , , , , , | Leave a comment

New CodeProject Article: Zero Footprint OCR with LEADTOOLS HTML5

Performing Optical Character Recognition (OCR) on mobile devices has always been a challenge due to the minimal processing power and storage space. However, with LEADTOOLS’ new HTML5 viewer and RESTful Web Services, it’s easier than ever. Take a look at our latest CodeProject article which covers how to display an image in our HTML5 Image Viewer control and call our OCR RESTful Web Service to perform either zonal or full-page OCR.

Another awesome feature I’ve yet to cover on our blog is the HTML5 Viewer’s RubberBand interactive mode. In this demo we allow the user to select a rectangle on the image, handle the event and then pass that rectangle as a zone to the OCR service. This event works flawlessly with both touchscreen finger swipe and mouse click and drag input and couldn’t have been easier to create a fast, powerful, truly cross platform OCR application.

Thanks,
Otis Goodwin

Posted in HTML5, OCR | Tagged , , , , , , , | Leave a comment

A Sneak Peek at What’s Coming for HTML5…

Back in May, LEAD released its first HTML5 toolkit. While some releases add to or optimize existing products, this release featured a completely new SDK, which has generated a lot of excitement. HTML5 has been a hot topic in the developer community, so we were anxious to provide a toolkit that leveraged our many years of imaging development experience for the HTML and JavaScript community. From the beta program to the official release, our HTML5 SDK has had an overwhelming amount of interest. This interest has generated a lot of incredible feedback, which we have used to improve on the initial release.

I sat in on an engineering meeting today and was able to get a sneak peek of what’s coming in the next week or two. Whether you are designing a mobile friendly PACS client, a document imaging application, or a basic image viewer, the next update will have something for you. The list was pretty long, so I’ve hand-picked a few of the best features and updates coming in the next week or so.

General Image Viewer

  • Scroll and mouse button support for the viewer
  • Scale-to-gray interpolation when resizing an image
  • Client side save support to PNG
  • Drag/Drop support
  • Large image support

Medical Viewer

  • Cine playback support
  • Window level speed enhancements
  • Signed image support
  • Modality, linear modality, VOI, and linear VOI LUT support
  • Min/Max bit, high bit, and bits allocated support
  • Custom palette for window level
  • Gesture sensitivity support for window level
  • Option to reset the window level to original values
  • Photometric interpretation support
  • Role management support
  • Session timeout support
  • Mechanism to control the number of concurrent sessions
As you can see, we have packed quite a bit into this next release planned. There are even more features planned for later this summer. Stay tuned for more updates, videos and release dates.

UPDATE: This update has been released and is now available for download!

Thanks,
Otis Goodwin

Posted in HTML5 | Tagged , , , , , , , , , | 2 Comments

New CodeProject Article: HTML5 Zero Footprint DICOM Viewer Annotations

There are many important aspects of a complete Zero Footprint DICOM Viewer built using HTML5 and JavaScript. The latest medical imaging development product from LEADTOOLS has it all: image display, image processing, fast client-side window leveling, series stack, annotations and more. In our first article, we introduced the viewer and highlighted its PACS query / retrieve and client-side window leveling features. In our latest article hosted on CodeProject, we took a closer look at the new Zero Footprint HTML5 DICOM Viewer’s annotation and markup features.

To complement this article we have also released two new video tutorials which show how to set up the and use the demo: Thanks,
Otis Goodwin

Posted in HTML5 | Tagged , , , , , , | Leave a comment

Annotate Any Canvas with HTML5

Screenshot of Annotations on a custom canvas
We have seen a lot of interest in our HTML5 SDK, and I recently had a customer ask about using our annotation framework on their own viewer or canvas. If you read my recent post about using LEADTOOLS HTML5 annotations in Windows Metro, you will notice that one of the arguments to the constructor for the main AnnAutomation object is an instance of an object which implements IAnnAutomationControl. In that particular demo, we used the ImageViewerAutomationControl which was attached to a LEADTOOLS ImageViewer, but since the viewer is based on the HTML5 canvas it is possible to use other LEADTOOLS features such as annotations on ANY canvas.

The IAnnAutomationControl interface is basically the glue between the drawing surface and the internal annotation framework. In order to accurately draw annotations, the framework needs to know about things like mouse and touch events, surface resolution, surface transforms, etc. The ImageViewerAutomationControl object is nothing more than a class which implements the IAnnAutomationControl interface, but it is specifically designed to be used with our image viewer control.

Back to the customer’s question, “If I have my own canvas that I am using to display image data but I still want to use the LEADTOOLS automated annotation framework, how do I attach the automation object to my canvas?” Even though you don’t use ImageViewerAutomationControl, it’s still pretty simple: (1) create your own class which implements IAnnAutomationControl and (2) pass that to the AnnAutomation constructor. I have attached the complete source code for this example at the end of this post but let’s go over a few key items in the interface first.

The most important pieces of information information for the annotation framework are the user initiated events occurring on the drawing surface. The easiest way to provide this information is to use the LEADTOOLS InteractiveService, which handles the task of interpreting mouse, touch, drag, pinch, and other types of events regardless of the type of browser or device we are running on. For example, the IAnnAutomationControl interface requires we implement PointerDown, PointerUp, and PointerMove events. To make things easy, I used the IntractiveService to notify me of all the various events that could be interpreted as PointerDown, PointerUp, and PointerMove so that I could pass them on to the underlying framework.

_service_DoubleTap: function Leadtools_Annotations_Automation_MyAutomationControl$_service_DoubleTap(sender, e) {
   if (this.__automationDoubleClick != null) {
      this.__automationDoubleClick(this, 
         Leadtools.Annotations.Core.AnnPointerEventArgs.create(
            Leadtools.Annotations.Core.AnnMouseButton.left, 
            Leadtools.LeadPointD.get_empty())
      );
   }
},

_service_DragCompleted: function Leadtools_Annotations_Automation_MyAutomationControl$_service_DragCompleted(sender, e) {
   if (this.__automationPointerUp != null) {
      this.__automationPointerUp(
         this, Leadtools.Annotations.Core.AnnPointerEventArgs.create(
            Leadtools.Annotations.Core.AnnMouseButton.left, 
            this.get_automationContainer().get_mapper().pointToContainerCoordinates(
               Leadtools.LeadPointD.create(
                  e.get_position().get_x() - e.get_change().get_x(), 
                  e.get_position().get_y() - e.get_change().get_y()
               )
            )
         )
      );
   }
},

_service_DragDelta: function Leadtools_Annotations_Automation_MyAutomationControl$_service_DragDelta(sender, e) {
   if (this.__automationPointerMove != null) {
      this.__automationPointerMove(this, 
         Leadtools.Annotations.Core.AnnPointerEventArgs.create(
            Leadtools.Annotations.Core.AnnMouseButton.left, 
            this.get_automationContainer().get_mapper().pointToContainerCoordinates(
               Leadtools.LeadPointD.create(
                  e.get_position().get_x() - e.get_change().get_x(), 
                  e.get_position().get_y() - e.get_change().get_y())
            )
         )
      );
   }
},

_service_DragStarted: function Leadtools_Annotations_Automation_MyAutomationControl$_service_DragStarted(sender, e) {
   if (!this._hasFocus) {
      this.handleGotFocus(null);
   }
   if (this.__automationPointerDown != null) {
      this.__automationPointerDown(this, 
         Leadtools.Annotations.Core.AnnPointerEventArgs.create(
            Leadtools.Annotations.Core.AnnMouseButton.left, 
            this.get_automationContainer().get_mapper().pointToContainerCoordinates(
               Leadtools.LeadPointD.create(
               e.get_position().get_x() - e.get_change().get_x(), 
               e.get_position().get_y() - e.get_change().get_y())
            )
         )
      );
   }
},

Since the framework will also handle rendering the annotations on your drawing surface, it must also be informed about any transforms which have been applied to the underlying drawing surface (scale, rotate, translate). The same holds true about the resolution; for the purpose of simplicity, we use 96 DPI and an Identity matrix for this demo.

get_automationXResolution: function Leadtools_Annotations_Automation_MyAutomationControl$get_automationXResolution() {
   return 96;
},

get_automationYResolution: function Leadtools_Annotations_Automation_MyAutomationControl$get_automationYResolution() {
   return 96;
}, 
get_automationTransform: function Leadtools_Annotations_Automation_MyAutomationControl$get_automationTransform() {
   //For this example, we will use the identity matrix. 
   //If you make any changes to the matrix of your background canvas, 
   //you should apply that same matrix here.
   return Leadtools.LeadMatrix.get_identity();
},

The next step is to create a canvas for the annotations, which is done internally. It is important that the canvas on which the annotations are rendered and the background canvas are maintained as separate canvases so that manipulating one does not affect the other. For example, if we need to clear all of the annotations, we do not want to affect any objects rendered on the background surface. Rendering in the annotation framework is handled by the AnnHtml5RenderingEngine object. So in our class, we create an instance of the renderer and utilize it within the AutomationInvalidate function to render the annotations to our canvas.

//Create a canvas for the annotation container. We will render the annotations directly to this container
this._annotationCanvas = document.createElement('canvas');
this._annotationCanvas.width = backgroundCanvas.width;
this._annotationCanvas.height = backgroundCanvas.height;
this._annotationCanvas.id = backgroundCanvas.id + '_annotationCanvas';
this._annotationCanvas.style.pixelLeft = 0;
this._annotationCanvas.style.pixelTop = 0;
this._annotationCanvas.style.marginTop = '0px';
this._annotationCanvas.style.marginRight = 'auto';
this._annotationCanvas.style.marginBottom = '0px';
this._annotationCanvas.style.marginLeft = 'auto';
this._annotationCanvas.style.position = 'absolute';
this._annotationCanvas.style.zIndex = 100;

//Add our annotation container to the parent of the background canvas
backgroundCanvas.parentNode.appendChild(this._annotationCanvas);
//Get the context for our canvas
this._context = this._annotationCanvas.getContext('2d');

//The automation object is attaching. We will create our renderer here.
automationAttach: function Leadtools_Annotations_Automation_MyAutomationControl$automationAttach(container) {
   this._container = container;
   if (this._annotationCanvas != null) {
      if (this._context != null) {
         //Create a new rendererer that will render to our annotation canvas
         this._engine = new Leadtools.Annotations.Rendering.AnnHtml5RenderingEngine(
               this._container, this._context, false);
         if (this._engine != null) {
            this._engine.render(Leadtools.LeadRectD.create(0, 0, 
                  this._annotationCanvas.width, this._annotationCanvas.height), true);
         }
      }
   }
},

//Render the annotations to our canvas
automationInvalidate: function Leadtools_Annotations_Automation_MyAutomationControl$automationInvalidate(rc) {
   if (this._engine != null) {
      this._engine.render(Leadtools.LeadRectD.create(0, 0, 
         this._annotationCanvas.width, this._annotationCanvas.height), true);
   }
},

Even though you are using a non-LEADTOOLS object for the canvas, it is still possible to permanently realize (burn in) the annotation to the drawing surface by implementing getImageData and putImageData. This is very important if you plan on using the redaction object, which is often used to hide confidential information in a document. It is also possible to restore the data which was overwritten when the redaction was realized. In order to do this, your class must handle replacing the image data on your drawing surface for realizing, and returning the image data under the redact object for restoring.

getImageData: function Leadtools_Annotations_Automation_MyAutomationControl$getImageData(rc) {
   if (rc.get_isEmpty()) {
      return null;
   }
   
   var context = this._backgroundCanvas.getContext('2d');
   return context.getImageData(parseInt(rc.get_left()), parseInt(rc.get_top()), 
      parseInt(rc.get_width()), parseInt(rc.get_height()));
},

putImageData: function Leadtools_Annotations_Automation_MyAutomationControl$putImageData(data, position) {
   if (position.get_isEmpty() || data == null) {
      return;
   }
   
   var context = this._backgroundCanvas.getContext('2d');
   var pixelData = data.data;
   context.putImageData(data, parseInt(position.get_x()), parseInt(position.get_y()));
}

You can download the complete source for this demo here. The class we discussed in the article can be found in ‘Scripts\MyAutomationControl.js’. If you have any questions about this article, feel free to contact us at support@leadtools.com.

Thanks,
Otis Goodwin

Posted in HTML5 | Tagged , , , , , , | 4 Comments