Looking to analyze and outline the minimum bounding rectangle (MBR) of an object captured by a camera in a 2D projection. In the image below, you can see the MBR of a cube object. I manually sketched the MBR (red rectangle) based on visual estimation. Is there a way to programmatically calculate the MBR (dimension and position) and display it in real-time on the camera image canvas?
https://i.sstatic.net/0rJn8.jpg
Edit: I managed to add an MBR to my example using a similar solution. However, the MBR is accurately drawn on a 2D overlay canvas, but when I move the camera (via mouse), the MBR shifts and the size becomes incorrect. Is there a flaw in the calculation or is it a bug in my implementation?
Note: My goal is not just to visualize the MBR, but also to determine the 2D coordinates and size on the camera image.
function computeScreenSpaceBoundingBox(mesh, camera) {
var vertices = mesh.geometry.vertices;
var vertex = new THREE.Vector3();
var min = new THREE.Vector3(1, 1, 1);
var max = new THREE.Vector3(-1, -1, -1);
for (var i = 0; i < vertices.length; i++) {
var vertexWorldCoord = vertex.copy(vertices[i]).applyMatrix4(mesh.matrixWorld);
var vertexScreenSpace = vertexWorldCoord.project(camera);
min.min(vertexScreenSpace);
max.max(vertexScreenSpace);
}
return new THREE.Box2(min, max);
}
function normalizedToPixels(coord, renderWidthPixels, renderHeightPixels) {
var halfScreen = new THREE.Vector2(renderWidthPixels/2, renderHeightPixels/2)
return coord.clone().multiply(halfScreen);
}
const scene = new THREE.Scene();
const light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 1, 1, 0.5 ).normalize();
scene.add( light );
scene.add(new THREE.AmbientLight(0x505050));
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshLambertMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
const renderWidth = window.innerWidth;
const renderHeight = window.innerHeight;
const camera = new THREE.PerspectiveCamera( 75, renderWidth / renderHeight, 0.1, 1000 );
camera.position.z = 10;
const controls = new THREE.OrbitControls( camera );
controls.update();
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const clock = new THREE.Clock();
const overlayCanvas = document.getElementById('overlay');
overlayCanvas.width = renderWidth;
overlayCanvas.height = renderHeight;
const overlayCtx = overlayCanvas.getContext('2d');
overlayCtx.lineWidth = 4;
overlayCtx.strokeStyle = 'red';
const animate = function () {
const time = clock.getElapsedTime() * 0.5;
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
cube.position.x = Math.sin(time) * 5
controls.update();
renderer.render( scene, camera );
const boundingBox2D = computeScreenSpaceBoundingBox(cube, camera);
// Convert normalized screen coordinates [-1, 1] to pixel coordinates:
const {x: w, y: h} = normalizedToPixels(boundingBox2D.getSize(), renderWidth, renderHeight);
const {x, y} =
normalizedToPixels(boundingBox2D.min, renderWidth, renderHeight)
.add(new THREE.Vector2(renderWidth / 2, renderHeight / 2));
overlayCtx.clearRect(0, 0, renderWidth, renderHeight);
overlayCtx.strokeRect(x, y, w, h);
};
animate();
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
#overlay {
position: absolute;
}
<body>
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2b5f43594e4e6b1b051a1b1f051b">[email protected]</a>/build/three.min.js"></script>
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cfbba7bdaaaa8fffe1fefffbe1ff">[email protected]</a>/examples/js/Detector.js"></script>
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f2869a809797b2c2dcc3c2c6dcc2">[email protected]</a>/examples/js/controls/OrbitControls.js"></script>
<canvas id="overlay"></canvas>
</body>
Note: Please provide feedback if my question requires further clarification for a more detailed explanation.