Guide on creating a 4-point perspective transform with HTML5 canvas and three.js

First off, here's a visual representation of my objective:

https://i.stack.imgur.com/5Uo1h.png

(Credit for the photo: )

The concise question

How can I use HTML5 video & canvas to execute a 4-point perspective transform in order to display only the "TV screen" portion of the frame on the canvas? Additionally, why does my current implementation not show the correct area?

Insight into my goal

I am in the process of constructing a webpage with the following functionalities:

  1. The user aims their webcam at a TV which may be positioned at any angle within the frame.
  2. The webcam footage is captured and previewed on the webpage using HTML5 video & canvas.
  3. The user defines the positions of the 4 corners of the TV screen by clicking on the preview (creating 4 pairs of x/y coordinates).
  4. ** The challenge lies in warping the video via some type of perspective transform so that the canvas exclusively displays the content within the actual TV screen, not the complete webcam view. **
  5. Following the transformation, image processing tasks are carried out such as identifying predominant colors. These processes do not fall within the scope of this query, except that I will eventually need access to the content/pixels of an HTML5 canvas.

My hurdle lies in step 4. To ensure that only the relevant part of each video frame is processed, it's crucial to "warp" the image to solely exhibit the "TV screen" region instead of the entire webcam feed.

Upon research, I've come to understand that:

  • This necessitates a form of perspective transform, given the non-parallel alignment due to various angles the webcam could be set at; thus, a 3-dimensional transform is imperative over a 2D transform. A 2D transform wouldn't address the issue of converging sides effectively.
  • HTML5 canvas is limited to two-dimensional transformations, rendering a 3D transform unfeasible. Since a solution compatible with canvas is sought, leveraging a 3D CSS transform like https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix3d is impractical. Perhaps delving into WebGL is necessary to accommodate the 3D aspect.

My Approach So Far

To tackle this, I pursued the subsequent method:

a) Capture webcam footage using a video tag.

b) Utilize three.js to construct a 3D scene rendered onto a canvas element allowing subsequent image processing.

c) The three.js scene involves: - a flat mesh displaying the video on one side through a VideoTexture. - an initial perspective camera configuration showcasing the entire webcam image.

d) Enable users to designate the four corner points indicating the TV location, compute respective x/y coordinates, and save them.

e) Compute a perspective transform stretching the image to fill the viewport based on the clicked "TV corner" points; I've been relying on this library: https://github.com/jlouthan/perspective-transform.

f) By applying the applicable transformation to the video-containing mesh while maintaining a fixed camera stance, I anticipate obtaining the desired image on the output canvas from a 2D viewpoint.

Link to My Current (Faulty) Implementation

Here's a link to my ongoing attempt outlined above. It permits video display and corner-clicking functionality. While successful around the center origin, issues arise when selecting other areas within the image.

https://bitbucket.org/mattwilson1024/perspective-transform/src/master/

In Conclusion

Your insights on why this isn't functioning as intended or suggestions for alternative/easier approaches to accomplish my requirements would be greatly appreciated.

Answer №1

The issue with the initial setup lies in how transformMatrix was being constructed.

I managed to resolve it by making the following adjustment:

transformMatrix.set(a1, a2, 0, a3, 
                    b1, b2, 0, b3, 
                    0,  0,  0, 1, 
                    c1, c2, 0, c3);

This solution was inspired by reading this answer on the Math StackExchange.

To assist anyone who may come across this problem in the future, I have revised the original question to reference an archive branch containing the faulty code. The corrected version can be accessed here.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue encountered: Incompatibility between Mongoose Populate and Array.push()

After reading a different post addressing the same issue, I still couldn't figure out how to implement the solution into my own scenario. The discussion revolved around the topic of node js Array.push() not working using mongoose. In my Mongoose asyn ...

Error: Attempting to access the property 'push' of an undefined variable has resulted in an unhandled TypeError

if (Math.random() <= .1) { let orgAdmin = User.find({email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1234454665324721380c0d">[email protected]</a>'}); or ...

Guide on creating a Jasmine test for a printer utility

Currently, I am working on writing a Jasmine test for the print function shown below: printContent( contentName: string ) { this._console.Information( `${this.codeName}.printContent: ${contentName}`) let printContents = document.getElementById( c ...

Clicking on an iframe activates the loading of the displayed page

I'm attempting to create a functionality where clicking on an iframe will load the page it is displaying. I experimented with placing it within an tag, but that didn't produce the desired result. The effect I'm aiming for is similar to zoom ...

Why is my filtering and sorting function failing to function properly?

I have a collection of events represented by an array of objects, where each event contains a start date and an end date. My goal is to filter out any events that have already passed based on the current time (now), and then sort the remaining events in d ...

What is the best way to determine the final letter of a column in a Google Sheet, starting from the first letter and using a set of

My current approach involves generating a single letter, but my code breaks if there is a large amount of data and it exceeds column Z. Here is the working code that will produce a, d: const countData = [1, 2, 3, 4].length; const initialLetter = 'A&a ...

Using Angular JS, send out a notification and pause execution until it is finished

I recently encountered an interesting situation involving an Angular event: $rootScope.$broadcast("postData"); doSomething(); However, I realized that doSomething() needs to wait for the completion of postData before executing. This led me to contemplate ...

In MUI v5, the Autocomplete default value is not set

When I try to use the defaultValue prop in the Autocomplete component of MUI v5, the value always ends up being undefined. This is a snippet from my code: const vehicles = [ { name: "Toyota", model: "Camry" }, { name: "Ford&qu ...

Even after trying to hide the legend in a Radar Chart using the configuration option `legend: {display: false}` in chart.js, the legend

Having trouble removing legend from Radar Chart in chart.js even when using legend: {display : false}. The code is being utilized and then displayed with HTML/JS. Here is the provided code snippet: var options5 = { type: 'radar', data: { ...

Error [ERR_UNSUPPORTED_DIR_IMPORT]: Nodejs App cannot be started locally due to a directory import issue

My journey to deploy my app on Heroku has hit a roadblock. The import statements, like import cors from 'cors', are causing the app to fail in production with the "Cannot Load ES6 Modules in Common JS" error. Interestingly, everything runs smooth ...

Enhance the "text" attribute of IXMLDOMElement to enable functionality in Chrome

The web application I am currently working on was developed approximately 10 years ago and is only compatible with Internet Explorer. My goal is to make it functional in Chrome as well. I am facing a challenge regarding the "text" property of IXMLDOMEleme ...

Show errors related to parsley within a bootstrap tooltip

I am currently working with Parsley 2.0.0-rc5 and I would like to display the error messages using a Bootstrap tooltip. The issue I am facing is that the "parsley:field:error" event fires before the error message is displayed in the error container, maki ...

Tips for maintaining a sticky header while continuing to utilize Bootstrap table classes such as table-responsive and table-stripped

This is Here's my code on jsfiddle I've attempted to make the header sticky while maintaining the current layout, but every approach I've tried ends up messing with the responsiveness of the table. My next plan involves using a JavaScript ...

sticky bootstrap datepicker anchored to the top of the screen

Currently, I am working on a form that includes a date picker using the bootstrap datepicker In this setup, I have hidden the main bootstrap field and added three custom fields. When any of these fields are clicked, the calendar should open next to them. ...

Discover the ins and outs of integrating YAML front matter into your destination directory path

I am looking to customize the path of my blog posts to include a fancy date format like /blog/2013/09/17 so that the links from my previous octopress blog remain intact. Within the YAML front matter on each markdown page, I have included the date informat ...

Does moment/moment-timezone have a feature that allows for the conversion of a timezone name into a more easily comprehendible format?

Consider this example project where a timezone name needs to be converted to a more readable format. For instance: input: America/Los_Angeles output: America Los Angeles While "America/Los_Angeles" may seem human-readable, the requirement is to convert ...

What is the best way to position a tooltip near an element for optimal visibility?

One div is located on the page as follows: <div id="tip"> Text for tip goes here... </div> And another one can be found below: <div class="element"> Text for element goes here... </div> There is also a piece of JavaScript ...

The requested property map cannot be found within the Array

I am working on a project using redux with react typescript. I have an external JSON file that contains employee data categorized by department id. To properly map the data with types in my application, I have created specific types. Check out this demo o ...

Discovering the properties of a class/object in a module with Node.js

Recently I delved into the world of node.js and found myself puzzled about how to discover the attributes, such as fields or properties, of a class or object from a module like url or http. Browsing through the official documentation, I noticed that it on ...

Ways to retrieve a variable within the init() function

My current project involves using datatables along with ajax to display information dynamically. Below is the code snippet I am working with: // Setting up the module var DatatableAdvanced = function() { // Examples of Basic Datatables var _c ...