How to crop images using bounding box - javascript

How do i crop an image while rendering to browser using the bounding box returned by aws rekognition indexFaces api? below is the bounding box example
Face: {
BoundingBox: {
Height: 0.16555599868297577,
Left: 0.30963000655174255,
Top: 0.7066670060157776,
Width: 0.22074100375175476
},
Confidence: 100,
FaceId: "29a75abe-397b-5101-ba4f-706783b2246c",
ImageId: "147fdf82-7a71-52cf-819b-e786c7b9746e"
},
Similarity: 97.04154968261719 }

I have faced the same issue. You have to perform some calculations on BoundingBox values. Note the estimated orientation returned in the response's OrientationCorrection field. It is the key value here. You have to find Top and Left from those confusing values. Here is the hint:
If OrientationCorrection = ROTATE_0
Left = image.width*BoundingBox.Left
Top = image.height*BoundingBox.To
If OrientationCorrection = ROTATE_90
Left = image.height * (1 - (BoundingBox.Top + .BoundingBox.Height))
Top = image.width * BoundingBox.Left
If OrientationCorrection = ROTATE_180
Left = image.width - (image.width*(BoundingBox.Left + BoundingBox.Width))
Top = image.height * (1 - (BoundingBox.Top + BoundingBox.Height))
If OrientationCorrection = ROTATE_270
Left = image.height * BoundingBox.top
Top = image.width * (1 - BoundingBox.Left - BoundingBox.Width)
Here is sample code of python which I have used.
import boto3
import io
from PIL import Image
# Calculate positions from from estimated rotation
def ShowBoundingBoxPositions(imageHeight, imageWidth, box, rotation):
left = 0
top = 0
if rotation == 'ROTATE_0':
left = imageWidth * box['Left']
top = imageHeight * box['Top']
if rotation == 'ROTATE_90':
left = imageHeight * (1 - (box['Top'] + box['Height']))
top = imageWidth * box['Left']
if rotation == 'ROTATE_180':
left = imageWidth - (imageWidth * (box['Left'] + box['Width']))
top = imageHeight * (1 - (box['Top'] + box['Height']))
if rotation == 'ROTATE_270':
left = imageHeight * box['Top']
top = imageWidth * (1- box['Left'] - box['Width'] )
print('Left: ' + '{0:.0f}'.format(left))
print('Top: ' + '{0:.0f}'.format(top))
print('Face Width: ' + "{0:.0f}".format(imageWidth * box['Width']))
print('Face Height: ' + "{0:.0f}".format(imageHeight * box['Height']))
if __name__ == "__main__":
photo='input.png'
client=boto3.client('rekognition')
#Get image width and height
image = Image.open(open(photo,'rb'))
width, height = image.size
print ('Image information: ')
print (photo)
print ('Image Height: ' + str(height))
print('Image Width: ' + str(width))
# call detect faces and show face age and placement
# if found, preserve exif info
stream = io.BytesIO()
if 'exif' in image.info:
exif=image.info['exif']
image.save(stream,format=image.format, exif=exif)
else:
image.save(stream, format=image.format)
image_binary = stream.getvalue()
response = client.detect_faces(Image={'Bytes': image_binary}, Attributes=['ALL'])
print('Detected faces for ' + photo)
for faceDetail in response['FaceDetails']:
print ('Face:')
if 'OrientationCorrection' in response:
print('Orientation: ' + response['OrientationCorrection'])
ShowBoundingBoxPositions(height, width, faceDetail['BoundingBox'], response['OrientationCorrection'])
else:
print ('No estimated orientation. Check Exif data')
print('The detected face is estimated to be between ' + str(faceDetail['AgeRange']['Low'])
+ ' and ' + str(faceDetail['AgeRange']['High']) + ' years')
print()
It will return the Top, Left, Height, Width of the face in the image. If you use python than you can easily crop image with PIL like this.
from PIL import Image
image = Image.open(open("Main_Image", 'rb'))
area = (Left, Top, Left + Width, Top + Height)
face = image.crop(area)
face.show()
It will show the cropped face from the image.
Happy Coding

Related

How to get drawline to work in Internet Explorer?

I have a function that draws a line inside a div (adapted from this solution), used in an e-learning module (explains the weird parts of the code).
My problem is that it doesn't seem to work in IE11.
Here is the code:
var player = GetPlayer();
var progress = player.GetVar("score");
var h = 600; //the height of your slide, as set in Story Size
var w = 1000; //the width of your slide, as set in Story Size
var t = 4; //the desired thickness of the progress bar in px, as based on the size of the styry slide
var c = "#00cc3399"; //the desired color of the progress bar, in hex. The last 2 digits are opacity. See codes below:
var startY = 40; //the desired start coordinate, from the top of the page
var startX = 0; //the desired start coordinate, from the left side of the page. Set to "0" as standard
var aspectRatio = h/w;
var totalW = $('.slide-container').width(); //get the actual width of the slide. only relevant if storyline is set to "Scale to fill browser window"
var totalH = totalW*aspectRatio; //calculate the actual height of the slide. only relevant if storyline is set to "Scale to fill browser window"
console.log("totalW: " + totalW);
var wFactor = totalW/w; //used to calculate the x-position on the slide. only relevant if storyline is set to "Scale to fill browser window"
var hFactor = totalH/h //used to calculate the y-position on the slide. only relevant if storyline is set to "Scale to fill browser window"
var endPointX = totalW*progress/100; //calculate the length of the progress bar
var endPointY = startY*hFactor; //calculate y-position, based on where you want it to be on the slide. The number is the position on the slide, as seen from the top. This is based on a horizontal progress bar
console.log("endPointX: " + endPointX + ", endPointY: " + endPointY)
x1 = startX, y1 = startY, //Start of the bar
x2 = endPointX, y2 = endPointY; //end of the bar
drawline(x1, y1, x2, y2);
//all the technical stuff:
function drawline(ax, ay, bx, by) {
console.log('ax: ' + ax);
console.log('ay: ' + ay);
console.log('bx: ' + bx);
console.log('by: ' + by);
if (ax > bx) {
bx = ax + bx;
ax = bx - ax;
bx = bx - ax;
by = ay + by;
ay = by - ay;
by = by - ay;
}
console.log('by: ' + by);
var angle = Math.atan((ay - by) / (bx - ax));
angle = (angle * 180 / Math.PI);
angle = -angle;
var length = Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by));
console.log('length: ' + length);
var style = ""
style += "left:" + (ax) + "px;"
style += "top:" + (ay) + "px;"
style += "width:" + length + "px;"
style += "height:"+t*hFactor+"px;" //set the thickness of the progress bar
style += "background-color:"+c+";"
style += "position:absolute;"
style += "transform:rotate(" + angle + "deg);"
style += "-ms-transform:rotate(" + angle + "deg);"
style += "transform-origin:0% 0%;"
style += "-moz-transform:rotate(" + angle + "deg);"
style += "-moz-transform-origin:0% 0%;"
style += "-webkit-transform:rotate(" + angle + "deg);"
style += "-webkit-transform-origin:0% 0%;"
style += "-o-transform:rotate(" + angle + "deg);"
style += "-o-transform-origin:0% 0%;"
style += "z-index:99;"
$("<div id='progressBar' style='" + style + "'></div>").appendTo('.slide-container');
};
In chrome this function draws a line based on the user's score. In IE nothing happens.
I found the solution myself. It turns out that IE redefined the 8-digit HEX value to RGBa, and turned it white. Replacing the 8-digit HEX value with it's corresponding RGBa value made it work.

Scaling and centering SVG with JS

Learning SVG / snap. With some help I can pull a region from a map and load it onto a modal per the fiddle here.
I am now trying to keep the SVG within a 200px by 200px div, maintaining its aspect ratio, and to scale, so each region in the modal will fit in the above box but remain to scale against other regions.
var m = Snap('#world-map');
var d = Snap('#svg-region');
var regionSVG = m.select('#' + region);
var p = regionSVG.clone();
d.append(p);
var bb = regionSVG.getBBox();
var vb = bb.x + ' ' + bb.y + ' ' + bb.width + ' ' + bb.height;
d.attr({
viewBox: vb
})
I tried processing the data in the bbox to create a scale, but failed miserably.
function zoomBBox(bbox)
{
var zbbox, ratio, diffx, diffy;
var length = 200;
if (bbox.width > bbox.height) {
ratio = bbox.width / length;
zbbox.width = length;
zbbox.height = bbox.height * ratio;
} else {
ratio = bbox.height / length;
zbbox.width = bbox.width * ratio;
zbbox.height = length;
}
diffx = (zbbox.width - length) / 2;
diffy = (zbbox.height - length) / 2;
zbbox.x = bbox.x + diffx;
zbbox.y = bbox.y + diffy;
return zbbox;
}
just add
.modal-body svg {
max-width: 100%;
max-height: 100%;
}
https://jsfiddle.net/0djhebes/7/
You just need the power of CSS :D
You need to define in the SVG #svg-region inside the modal via css HEIGHT & WIDTH that you want that they appear at their maximum.
For example 100px & 100px and they will be rendered inside that box
#svg-region{width:100px; height:100px;}
For aligning it, use a as wrapper around #svg-region with the following css (I think that should work, didn't test it to align vertically and horizontally)
.wrapper{display:flex; jusfity-alignment:center; align-item:center;}

Cropping an image with a preview using jcrop

I'm using jcrop and trying to make a "live" preview of the cropped area on an image.
The movement of the selected area works perfectly if the "Crop Selection" area is the same height and width as the destination preview div.
Check out the issue here: http://jsfiddle.net/fbaAW/
function showCoords(c)
{
var $this = this.ui.holder;
var original = $this.prev();
var preview = original.parent().find(".image");
var oH = original.height();
var oW = original.width();
var pH = preview.height();
var pW = preview.width();
var sH = c.h;
var sW = c.w;
var differenceH = pH - sH;
var differenceW = pW - sW;
//preview.css('width', c.w);
//preview.css('height', c.h);
//preview.css("background-size", Math.round(oW + differenceW) + "px" + " " + Math.round(oH + differenceH) + "px");
preview.css("background-position", Math.round(c.x) * -1 + "px" + " " + Math.round(c.y) * -1 + "px");
}
As you can see, I've commented out a few of my tests and attempts at getting this code to work properly but I just can't wrap my head around the relationship between the position and the size background properties in order to get this effect to work correctly.
Calculate the horizontal and vertical ratios between the selection size and the preview area size:
var rW = pW / c.w;
var rH = pH / c.h;
Then apply them to the background-size and background-position:
preview.css("background-size", (oW*rW) + "px" + " " + (oH*rH) + "px");
preview.css("background-position", rW * Math.round(c.x) * -1 + "px" + " " + rH * Math.round(c.y) * -1 + "px");
http://jsfiddle.net/fbaAW/1/
So, if the preview size is, say, 3 times the size of your jCrop selection area, it means you have scale the original image by 3, and compensate for the scaling when defining the background position.

Pinch to zoom with CSS3

I'm trying to implement pinch-to-zoom gestures exactly as in Google Maps. I watched a talk by Stephen Woods - "Creating Responsive HTML5 Touch Interfaces” - about the issue and used the technique mentioned. The idea is to set the transform origin of the target element at (0, 0) and scale at the point of the transform. Then translate the image to keep it centered at the point of transform.
In my test code scaling works fine. The image zooms in and out fine between subsequent translations. The problem is I am not calculating the translation values properly. I am using jQuery and Hammer.js for touch events. How can I adjust my calculation in the transform callback so that the image stays centered at the point of transform?
The CoffeeScript (#test-resize is a div with a background image)
image = $('#test-resize')
hammer = image.hammer ->
prevent_default: true
scale_treshold: 0
width = image.width()
height = image.height()
toX = 0
toY = 0
translateX = 0
translateY = 0
prevScale = 1
scale = 1
hammer.bind 'transformstart', (event) ->
toX = (event.touches[0].x + event.touches[0].x) / 2
toY = (event.touches[1].y + event.touches[1].y) / 2
hammer.bind 'transform', (event) ->
scale = prevScale * event.scale
shiftX = toX * ((image.width() * scale) - width) / (image.width() * scale)
shiftY = toY * ((image.height() * scale) - height) / (image.height() * scale)
width = image.width() * scale
height = image.height() * scale
translateX -= shiftX
translateY -= shiftY
css = 'translateX(' + #translateX + 'px) translateY(' + #translateY + 'px) scale(' + scale + ')'
image.css '-webkit-transform', css
image.css '-webkit-transform-origin', '0 0'
hammer.bind 'transformend', () ->
prevScale = scale
I managed to get it working.
jsFiddle demo
In the jsFiddle demo, clicking on the image represents a pinch gesture centred at the click point. Subsequent clicks increase the scale factor by a constant amount. To make this useful, you would want to make the scale and translate computations much more often on a transform event (hammer.js provides one).
The key to getting it to work was to correctly compute the point of scale coordinates relative to the image. I used event.clientX/Y to get the screen coordinates. The following lines convert from screen to image coordinates:
x -= offset.left + newX
y -= offset.top + newY
Then we compute a new size for the image and find the distances to translate by. The translation equation is taken from Stephen Woods' talk.
newWidth = image.width() * scale
newHeight = image.height() * scale
newX += -x * (newWidth - image.width) / newWidth
newY += -y * (newHeight - image.height) / newHeight
Finally, we scale and translate
image.css '-webkit-transform', "scale3d(#{scale}, #{scale}, 1)"
wrap.css '-webkit-transform', "translate3d(#{newX}px, #{newY}px, 0)"
We do all our translations on a wrapper element to ensure that the translate-origin stays at the top left of our image.
I successfully used that snippet to resize images on phonegap, using hammer and jquery.
If it interests someone, i translated this to JS.
function attachPinch(wrapperID,imgID)
{
var image = $(imgID);
var wrap = $(wrapperID);
var width = image.width();
var height = image.height();
var newX = 0;
var newY = 0;
var offset = wrap.offset();
$(imgID).hammer().on("pinch", function(event) {
var photo = $(this);
newWidth = photo.width() * event.gesture.scale;
newHeight = photo.height() * event.gesture.scale;
// Convert from screen to image coordinates
var x;
var y;
x -= offset.left + newX;
y -= offset.top + newY;
newX += -x * (newWidth - width) / newWidth;
newY += -y * (newHeight - height) / newHeight;
photo.css('-webkit-transform', "scale3d("+event.gesture.scale+", "+event.gesture.scale+", 1)");
wrap.css('-webkit-transform', "translate3d("+newX+"px, "+newY+"px, 0)");
width = newWidth;
height = newHeight;
});
}
I looked all over the internet, and outernet whatever, until I came across the only working plugin/library - http://cubiq.org/iscroll-4
var myScroll;
myScroll = new iScroll('wrapper');
where wrapper is your id as in id="wrapper"
<div id="wrapper">
<img src="smth.jpg" />
</div>
Not a real answer, but a link to a plug=in that does it all for you. Great work!
https://github.com/timmywil/jquery.panzoom
(Thanks 'Timmywil', who-ever you are)
This is something I wrote a few years back in Java and recently converted to JavaScript
function View()
{
this.pos = {x:0,y:0};
this.Z = 0;
this.zoom = 1;
this.scale = 1.1;
this.Zoom = function(delta,x,y)
{
var X = x-this.pos.x;
var Y = y-this.pos.y;
var scale = this.scale;
if(delta>0) this.Z++;
else
{
this.Z--;
scale = 1/scale;
}
this.zoom = Math.pow(this.scale, this.Z);
this.pos.x+=X-scale*X;
this.pos.y+=Y-scale*Y;
}
}
The this.Zoom = function(delta,x,y) takes:
delta = zoom in or out
x = x position of the zoom origin
y = y position of the zoom origin
A small example:
<script>
var view = new View();
var DivStyle = {x:-123,y:-423,w:300,h:200};
function OnMouseWheel(event)
{
event.preventDefault();
view.Zoom(event.wheelDelta,event.clientX,event.clientY);
div.style.left = (DivStyle.x*view.zoom+view.pos.x)+"px";
div.style.top = (DivStyle.y*view.zoom+view.pos.y)+"px";
div.style.width = (DivStyle.w*view.zoom)+"px";
div.style.height = (DivStyle.h*view.zoom)+"px";
}
function OnMouseMove(event)
{
view.pos = {x:event.clientX,y:event.clientY};
div.style.left = (DivStyle.x*view.zoom+view.pos.x)+"px";
div.style.top = (DivStyle.y*view.zoom+view.pos.y)+"px";
div.style.width = (DivStyle.w*view.zoom)+"px";
div.style.height = (DivStyle.h*view.zoom)+"px";
}
</script>
<body onmousewheel="OnMouseWheel(event)" onmousemove="OnMouseMove(event)">
<div id="div" style="position:absolute;left:-123px;top:-423px;width:300px;height:200px;border:1px solid;"></div>
</body>
This was made with the intention of being used with a canvas and graphics, but it should work perfectly for normal HTML layout

How to draw a line between two divs?

I'm currently trying to draw a diagonal line between the bottom right corner of one div to the top right corner of another. If possible, I would like to do it without jQuery. Is this possible?
http://jsfiddle.net/cnmsc1tm/
This won't work with IE8 or below because of CSS limitations.
function getOffset( el ) {
var rect = el.getBoundingClientRect();
return {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
width: rect.width || el.offsetWidth,
height: rect.height || el.offsetHeight
};
}
function connect(div1, div2, color, thickness) { // draw a line connecting elements
var off1 = getOffset(div1);
var off2 = getOffset(div2);
// bottom right
var x1 = off1.left + off1.width;
var y1 = off1.top + off1.height;
// top right
var x2 = off2.left + off2.width;
var y2 = off2.top;
// distance
var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)));
// center
var cx = ((x1 + x2) / 2) - (length / 2);
var cy = ((y1 + y2) / 2) - (thickness / 2);
// angle
var angle = Math.atan2((y1-y2),(x1-x2))*(180/Math.PI);
// make hr
var htmlLine = "<div style='padding:0px; margin:0px; height:" + thickness + "px; background-color:" + color + "; line-height:1px; position:absolute; left:" + cx + "px; top:" + cy + "px; width:" + length + "px; -moz-transform:rotate(" + angle + "deg); -webkit-transform:rotate(" + angle + "deg); -o-transform:rotate(" + angle + "deg); -ms-transform:rotate(" + angle + "deg); transform:rotate(" + angle + "deg);' />";
//
// alert(htmlLine);
document.body.innerHTML += htmlLine;
}
The Distance Formula
Finding the Center Of Two Points
Finding the Angle Between Two Points
CSS Transform:Rotate
HTML Element offset[Width|Height|Top|Left] properties
Edit (for others with the same problem):
If you need to, for example, create a line from two corners that are not the top right and bottom right divs, go to this section of the code:
// bottom right
var x1 = off1.left + off1.width;
var y1 = off1.top + off1.height;
// top right
var x2 = off2.left + off2.width;
var y2 = off2.top;
where you see + off1.width and + off1.height, that means that the code is calculating the position of the bottom or the right of the div. Remove the + off1.width or the + off1.height to get the left or the top of the div.
EDIT updated to a more standard getOffset function. If you want to get really anal you'd probably also have to add document.documentElement.client[Left/Top] and walk the offsetParent tree, but I think getBoundingClientRect() and window.page[X/Y]Offset are sufficient for an example like this.
There is a way to do it without jQ.
Find the position of your divs using offset.
Find the slope
draw 1x1px points from start to end position using the slope in your loop.

Categories

Resources