I am trying to draw bounding boxes on a face but there is some issue with the CSS which shows the following error :
[269,"RCTView",281,{"position":"absolute","borderWidth":2,"borderColor":-65536,"left":"<<NaN>>","top":"<<NaN>>","width":"<<NaN>>","height":"<<NaN>>"}] is not usable as a native method argument
Style for bounding box where the error is coming from:
bbox: {
position: "absolute",
borderWidth: 2,
borderColor: "red"
},
Function to render the Bounding Box on face:
const previewLeft = 40;
const previewTop = 50;
const previewWidth = 350;
const previewHeight = 600;
const tensorDims = { height: 224, width: 224, depth: 3 };
const renderBoundingBoxes = () => {
const { faces } = modelFaces;
const tensorDims = { height: 300, width: 400 };
const scale = {
height: styles.camera.height / tensorDims.height,
width: styles.camera.width / tensorDims.width
};
const flipHorizontal = Platform.OS === "ios" ? false : true;
if (!isEmpty(faces)) {
return faces.map((face, i) => {
const { topLeft, bottomRight } = face;
const bbLeft = topLeft[0] * scale.width;
const boxStyle = Object.assign({}, styles.bbox, {
left: flipHorizontal
? previewWidth - bbLeft - previewLeft
: bbLeft + previewLeft,
top: topLeft[1] * scale.height + 20,
width: (bottomRight[0] - topLeft[0]) * scale.width,
height: (bottomRight[1] - topLeft[1]) * scale.height
});
return <View style={boxStyle} key={`face${i}`}></View>;
1;
});
}
};
RenderBoundingBoxes being called:
<View>
<TensorCamera
style={styles.camera}
type={type}
cameraTextureHeight={textureDims.height}
cameraTextureWidth={textureDims.width}
resizeHeight={tensorDims.height}
resizeWidth={tensorDims.width}
resizeDepth={tensorDims.depth}
onReady={images => handleCameraStream(images)}
autorender={AUTORENDER}
/>
{renderBoundingBoxes()}
</View>
I made a mistake, bottomRight and topLeft are not arrays but tensor objects instead.
I needed to download the tensors using tf.dataSync() first then access the arrays within them:
const renderBoundingBoxes = () => {
const { faces } = modelFaces;
const scale = {
height: styles.camera.height / tensorDims.height,
width: styles.camera.width / tensorDims.width
};
const flipHorizontal = Platform.OS === "ios" ? false : true;
if (!isEmpty(faces)) {
return faces.map((face, i) => {
const { topLeft, bottomRight } = face;
const bbLeft = topLeft.dataSync()[0] * scale.width;
const boxStyle = Object.assign({}, styles.bbox, {
left: flipHorizontal
? previewWidth - bbLeft - previewLeft
: bbLeft + previewLeft,
top: topLeft.dataSync()[1] * scale.height + 20,
width:
(bottomRight.dataSync()[0] - topLeft.dataSync()[0]) * scale.width,
height:
(bottomRight.dataSync()[1] - topLeft.dataSync()[1]) * scale.height
});
return <View style={boxStyle}></View>;
1;
});
}
};
Related
I am trying to serialize/deserialize a custom farbicJS object that is subclassed from fabric.Textbox, but when loading the object back into the canvas from the saved JSON, the custom object gets restored with the wrong attributes, and it no longer behaves the way that it should when resizing the object.
Here is my custom object that is used to create a textbox with padding, and fromObject method I tried to use below:
EDITED: I fixed the initial height issue by refactoring the toObject method to include _renderBackground and padding
fabric.TextboxWithPadding = fabric.util.createClass(fabric.Textbox, {
type: 'TextboxWithPadding',
_renderBackground: function (ctx) {
if (!this.backgroundColor) {
return;
}
var dim = this._getNonTransformedDimensions();
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(
-dim.x / 2 - this.padding,
-dim.y / 2 - this.padding,
dim.x + this.padding * 2,
dim.y + this.padding * 2
);
this._removeShadow(ctx);
},
toObject: function () {
return fabric.util.object.extend(this.callSuper('toObject'), {
_renderBackground: this._renderBackground,
padding: this.padding
});
},
});
fabric.TextboxWithPadding.fromObject = function (object, callback) {
return fabric.Object._fromObject("TextboxWithPadding", object, callback, 'text');
};
When adding the TextboxWithPadding I am using the following:
if (drawingObject.type === "label") {
canvas.selection = false;
var pointer = canvas.getPointer(options.e);
origX = pointer.x;
origY = pointer.y;
label = new fabric.TextboxWithPadding('New Label', {
selectable: true,
fill: '#555555',
left: origX,
top: origY,
lockRotation: true,
fontSize: 18,
fontWeight: 400,
backgroundColor: '#ffffff',
width: 100,
height: 100,
textAlign: 'center',
fontFamily: 'Open Sans',
padding: 10
})
label.setControlsVisibility({
mt: false,
bl: false,
br: false,
tl: false,
tr: false,
mb: true,
});
canvas.add(label);
let lastHeight;
let calcFont;
const updateTextSize = () => {
const controlPoint = label.__corner;
//mr and ml are the only controlPoints that don't modify textbox height
if (controlPoint && controlPoint != "mr" && controlPoint != "ml") {
lastHeight = label.height * label.scaleY;
calcFont = lastHeight * .8
if (calcFont < 10) {
calcFont = 10
}
} else {
lastHeight = label.height
calcFont = label.fontSize
}
label.set({
height: lastHeight || label.height,
scaleY: 1,
scaleX: 1,
fontSize: calcFont
});
canvas.renderAll();
};
label.on('scaling', updateTextSize);
// Handle textbox content updates
canvas.on('text:changed', function () {
label.set('width', label.width)
$(".deleteBtn").remove();
});
canvas.on('mouse:up', function (options) {
if (drawingObject.type === "label") {
drawingObject.type = ''
}
canvas.renderAll();
canvas.selection = true;
});
}
Here is the result of saving this object to JSON (excluding the backgroundImage data url):
{"version":"2.4.0","objects":[{"type":"TextboxWithPadding","version":"2.4.0","originX":"left","originY":"top","left":49.5,"top":49,"width":100,"height":20.34,"fill":"#555555","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"#ffffff","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"text":"New Label","fontSize":18,"fontWeight":400,"fontFamily":"Open Sans","fontStyle":"normal","lineHeight":1.16,"underline":false,"overline":false,"linethrough":false,"textAlign":"center","textBackgroundColor":"","charSpacing":0,"minWidth":20,"styles":{}}]}
You can see how the height changes from the default 100 to 20.34 as well. I'm thinking that the event handlers need to be added to the custom object when it is loaded back into the canvas, but I'm not entirely sure.
Any help with this is greatly appreciated, thank you.
Alright, after messing around with this some more, I figured it out.
I needed to add in my custom methods and properties to toObject class method like so:
// ...
toObject: function () {
return fabric.util.object.extend(this.callSuper('toObject'), {
_renderBackground: this._renderBackground,
padding: this.padding,
});
},
// ...
and to add the listeners back to the restored object I added a 'reviver function' as the thrid argument to canvas.loadFromJSON which allows me to access the objects added to the canvas from JSON:
$("#load-from-json").click(function () {
canvas.loadFromJSON(
JSON.parse(canvasSavedJSON),
canvas.renderAll.bind(canvas),
function (o, object) {
if (object.type == 'TextboxWithPadding') {
object.setControlsVisibility({
mt: false,
bl: false,
br: false,
tl: false,
tr: false,
mb: true,
});
let lastHeight;
let calcFont;
const updateTextSize = () => {
const controlPoint = object.__corner;
//mr and ml are the only controlPoints that dont modify textbox height
if (controlPoint && controlPoint != "mr" && controlPoint != "ml") {
lastHeight = object.height * object.scaleY;
calcFont = lastHeight * .8
if (calcFont < 10) {
calcFont = 10
}
} else {
lastHeight = object.height
calcFont = object.fontSize
}
object.set({
height: lastHeight || object.height,
scaleY: 1,
scaleX: 1,
//padding: lastHeight,
fontSize: calcFont
});
canvas.renderAll();
};
object.on('scaling', updateTextSize);
}
}
);
});
I'm trying to fit the game to the screen, however I'm unable to scale the stage itself. With libraries like EaselJS from CreateJS I can simply do stage.scaleX = stage.scaleY = r.
In Phaser, I want to scale not only the game, but also together a HTML div (for user interface) that is outside Phaser. As you can see, the drawn rectangle is not scaled according to the current screen size, no matter what properties I change (game.scale.scaleMode, game.scale.displayScale etc.). Is there anything I can do?
const ProjectSettings = {
backgroundColor: 0x333333,
resolution: [100, 100],
};
const SceneContainers = {
sceneContent: null,
sceneCanvas: null,
phaserGame: null,
sceneUIDiv: null,
init() {
SceneContainers.sceneContent = document.getElementById('content');
SceneContainers.phaserGame = new Phaser.Game({
width: ProjectSettings.resolution[0],
height: ProjectSettings.resolution[1],
type: Phaser.AUTO,
parent: SceneContainers.sceneContent,
backgroundColor: ProjectSettings.backgroundColor,
scene: [Preloader],
});
SceneContainers.sceneCanvas = SceneContainers.phaserGame.canvas;
SceneContainers.sceneCanvas.classList.add('scene-canvas');
SceneContainers.sceneUIDiv = document.createElement('div');
SceneContainers.sceneUIDiv.id = 'scene-ui';
SceneContainers.sceneContent.appendChild(SceneContainers.sceneUIDiv);
},
};
const SceneScaler = {
init() {
SceneContainers.phaserGame.scale.scaleMode = Phaser.Scale.ScaleModes.RESIZE;
SceneScaler.fitToScreen();
window.addEventListener('resize', e => {
SceneScaler.fitToScreen();
});
},
fitToScreen() {
const [contentW, contentH] = ProjectSettings.resolution;
const [windowW, windowH] = [innerWidth, innerHeight];
const ratio = contentW / contentH;
const windowRatio = windowW / windowH;
let scale = windowW / contentW;
if (windowRatio > ratio) {
scale = windowH / contentH;
}
//SceneContainers.sceneCanvas.width = contentW * scale;
//SceneContainers.sceneCanvas.height = contentH * scale;
SceneContainers.phaserGame.scale.resize(contentW * scale, contentH * scale);
SceneContainers.sceneUIDiv.style.transform = `scale(${scale})`;
SceneContainers.sceneContent.style.width = `${contentW * scale}px`;
SceneContainers.sceneContent.style.height = `${contentH * scale}px`;
SceneContainers.sceneUIDiv.style.width = `${contentW}px`;
SceneContainers.sceneUIDiv.style.height = `${contentH}px`;
SceneContainers.sceneContent.style.left =
SceneContainers.sceneCanvas.style.left =
SceneContainers.sceneUIDiv.style.left = `${windowW / 2 - (contentW * scale) / 2}px`;
SceneContainers.sceneContent.style.top =
SceneContainers.sceneCanvas.style.top =
SceneContainers.sceneUIDiv.style.top = `${windowH / 2 - (contentH * scale) / 2}px`;
//SceneContainers.phaserGame.scale.displayScale = new Phaser.Math.Vector2(scale, scale);
},
};
class Scene extends Phaser.Scene {
constructor(id) {
super(id);
}
init() {
this.events.on('shutdown', () => {
SceneContainers.sceneUIDiv.innerHTML = '';
});
}
}
class Preloader extends Scene {
constructor() {
super('preloader');
}
preload() {
}
create() {
this.add.rectangle(50, 50, 50, 50, 0xff6666);
}
}
class EntryPoint {
constructor() {
this.init();
}
init() {
SceneContainers.init();
SceneScaler.init();
}
}
new EntryPoint;
html, body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
html, body {
background: #333333;
}
#content {
position: absolute;
display: inline-block;
}
.scene-canvas {
position: absolute;
}
#scene-ui {
display: inline-block;
position: absolute;
transform-origin: left top;
}
<script src="https://github.com/photonstorm/phaser/releases/download/v3.55.2/phaser.min.js"></script>
<div id="content"></div>
Also, for some reason my snippet is giving undefined for canvas.classList, so I appreciate if someone could fix that. This classList error is not happening with my project.
If you are only looking for automatic scaling, you could use the config parameter scale.mode (of the Phaser Game Object) and set it to Phaser.Scale.FIT, then everything should be scaled.
Here a very simplified demo:
If you resize the window (when you open the snipplet fullsize and change the height), you should see "the scaling"
var Preloader = {
create() {
this.add.rectangle(50, 50, 50, 50, 0xff6666);
}
};
new Phaser.Game({
width:300,
height:300,
scale: {
mode: Phaser.Scale.FIT,
},
type: Phaser.AUTO,
scene: [Preloader],
});
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.min.js"></script>
I am working on an app which the app is intented to capture/load an image, then get the correct color from the touched coordinate on that image / camera view. (Similar to iOS's Swatches app)
The libraries I am using is react-native-camera, react-native-get-pixel-color and react-native-draggable .
There is some logic issue I'm stucked on currently....and I have researched from online and still lost in the jungle
Ratio of every devices is not same so I could not have hardcoded on the ratio, based on the document stated that the ratio is only available for android but not for iOS (default will be 4:3 for android if not defined any). So I'm thinking is it better to not to set it ? Rely on pictureSize better ? Or can without both ? Any advices is welcome...
The part which I'm quite confusing now is on the calculation part, I wanted to get the correct part of the image's color, despite different ratio , device's screen size and image size:
Live view - always get from the center
Still view - based on draggable and get the coordinates
renderCamView = () => {
if (this.state.isEnabled) {
return (
<Image
source={{ uri: this.state.liveImg.data.uri }}
style={styles.imgPreview}
/>
);
} else {
return (
<RNCamera
ref={(ref) => {
this.camera = ref;
console.log('isref');
}}
onCameraReady={()=> this.getCamData()}
style={{
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: colors.black
}}
type={RNCamera.Constants.Type.back}
flashMode={this.getFlashMode()}
exposure={this.state.camera.exposure}
whiteBalance={this.state.camera.whiteBalance}
//ratio={'1:1'}
// pictureSize={this.state.pictureSize}
captureAudio={false}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
/>
);
}
};
renderCenterView = () => {
if(this.state.isEnabled){
const { liveImg, mainView } = this.state;
return <Draggable
x={(this.state.mainView.width/2)-20} y={(this.state.mainView.height/2)-20}
isCircle
onShortPressRelease={this.captureColor}
onDragRelease={this.onDragEvt}
minX={0}
minY={0}
maxX={this.state.mainView.width}
maxY={this.state.mainView.height}
>
<View
style={{ width:50, height:50,borderWidth:15, borderRadius: 50, borderColor:'#d1c1b6', opacity: 0.8 }}></View>
</Draggable>
}else{
return <TouchableOpacity
onPress={this.captureColor}
style={[styles.middlePoint, { top:(this.state.mainView.height/2)-20, left:(this.state.mainView.width/2)-20 }]}/>
}
}
// Logic on Draggable, to find the coordinates on image view
onDragEvt = (event, gestureState, bounds) => {
let sourceWidth = this.state.liveImg.width;
let sourceHeight = this.state.liveImg.height;
// Find view dimensions
let width = this.state.mainView.width;
let height = this.state.mainView.height;
// Assuming image has been scaled to the smaller dimension to fit, calculate the scale
let wScale = width / sourceWidth;
let hScale = height / sourceHeight;
let scale = Math.min(wScale, hScale);
// Find the offset in the direction the image doesn't fill the screen
// For ImageViews that wrap the content, both offsets will be 0 so this is redundant
let offsetX = 0;
let offsetY = 0;
if (wScale <= hScale) {
offsetY = height / 2 - (scale * sourceHeight) / 2;
} else {
offsetX = width / 2 - (scale * sourceWidth) / 2;
}
// Convert event coordinates to image coordinates
let sourceX = (gestureState.moveX - offsetX) / scale;
let sourceY = (gestureState.moveY - offsetY) / scale;
if (sourceX < 0) {
sourceX = 0;
} else if (sourceX > sourceWidth) {
sourceX = sourceWidth - 5;
}
if (sourceY < 0) {
sourceY = 0;
} else if (sourceY > sourceHeight) {
sourceY = sourceHeight - 5;
}
this.setState({ dragView: { x: sourceX, y: sourceY } });
};
getPixelByPercentage(percentage, widthOrHeight) {
return (percentage / 100) * widthOrHeight;
}
getPixel(imageData, x, y) {
try {
GetPixelColor.setImage(isIOS ? imageData.uri : imageData.base64).catch(
(err) => {
// Handle errors
console.error(err);
},
);
GetPixelColor.pickColorAt(x, y)
.then((color) => {
// from react-native-get-pixel-color
// 'color' return as HEX
})
.catch((err) => {
// Handle errors
console.error(err);
});
} catch (e) {}
}
captureColor = async () => {
if (this.state.isEnabled) {
// live view
const { dragView, liveImg } = this.state;
if (Object.keys(dragView).length !== 0) {
let sourceX = dragView.x;
let sourceY = dragView.y;
this.getPixel(liveImg.data, sourceX, sourceY);
} else {
let getPiX = this.getPixelByPercentage(50, liveImg.width);
let getPiY = this.getPixelByPercentage(50, liveImg.height);
this.getPixel(liveImg.data, getPiX, getPiY);
}
} else {
if (this.camera) {
const data = await this.camera.takePictureAsync(CAM_OPTIONS);
await Image.getSize(data.uri, (width, height) => {
console.log('W: ', width);
console.log('H: ', height);
this.setState({
capImage: {
data: data,
imageBase64: data.base64,
width: width,
height: height,
},
});
}).then((res) => {
const { capImage } = this.state;
let getPiX = this.getPixelByPercentage(50, capImage.width);
let getPiY = this.getPixelByPercentage(50, capImage.height);
this.getPixel(capImage.data, getPiX, getPiY);
});
}
}
};
NOTES:
1. renderCamView() - render either Camera or Image
2. renderCenterView() - render the center item, either is draggable in 'still' view or fixed touchable button in 'live' view to always get the center point
3. onDragEvt() - the Draggable's event to keep track the draggable item's movement
4. this.state.mainView - the state that holds width and height of the root <View /> of this screen
5. captureColor() - onPress event to get the pixel color
6. this.state.isEnabled - define it is still view / live view
Live view - (Camera view)
Still view - (Capture and display as image / image select from album)
*Sorry for my bad English, if there is any confusing part please let me know
I'm using fabricjs to make a ruler tool. When the user click on the canvas it will draw a small rectangle and a line (with length of 0) as the user starts to move the mouse the length of line increases. At the top of the line There's a text which will show the length of the line. The length of the line is updating But it won't display the updated length. It displays the updated length once I click again to finish drawing.
Here's my code
let oneSideDrawn = false;
canvas.on("mouse:down", (event) => {
function rulerDown() {
const mouse = canvas.getPointer(event.e);
const { x, y } = mouse;
const side = new fabric.Rect({
width: 3,
height: 16,
left: x,
top: y,
fill: "#222",
});
canvas.add(side);
updateCanvas();
if (oneSideDrawn) {
oneSideDrawn = false;
const objs = canvas.getObjects();
const length = objs.length;
const line = objs[length - 3];
const side2 = objs[length - 4];
side.set({ top: side2.top });
const txt = canvas.getById("myId");
const rulerGroup = new fabric.Group([side2, line, side, txt], {
basicStyle,
lockScalingX: true,
lockScalingY: true,
});
canvas.add(rulerGroup);
canvas.remove(side2);
canvas.remove(line);
canvas.remove(side);
canvas.remove(txt);
canvas.setActiveObject(rulerGroup);
updateCanvas();
updateMode("");
} else {
const line = new fabric.Rect({
width: 0,
height: 3,
left: x,
top: y + 6,
fill: "#222",
});
const txt = new fabric.Text("0 m", {
fontSize: 10,
left: line.left + line.width / 2,
top: side.top - 5,
id: "myId",
});
txt.set({ left: txt.left - txt.width / 2 });
canvas.add(line);
canvas.add(txt);
updateCanvas();
canvas.setActiveObject(line);
oneSideDrawn = true;
}
}
}
canvas.on("mouse:move", (event) => {
function rulerMove() {
const line = canvas.getActiveObject();
const lineTxt = canvas.getById("myId");
const mouse = canvas.getPointer(event.e);
const { x } = mouse;
const width = Math.abs(x - line.left);
if (!width) {
return false;
}
line.set({ width });
lineTxt.set({
text: parseFloat(width).toFixed(2) + " m",
left: line.left + width / 2 - 15,
});
updateCanvas();
}
}
Here's a screen shot
As we can see the text is updating in the console.log But the value is still "0 m" on the canvas
Update fabricjs!
I was facing this issue when I was using fabricjs 4.3.0.
In fabricjs 4.4.0 they solved this issue
I want to make and dynamic crop area and found this snippet. It works perfect in normal usage, but when you scaled the original object before making the crop area, the crop zone seems not in the right position. Can you look into this pen for some help ?
var canvas = new fabric.CanvasEx('canvas');
var el;
var object, lastActive, object1, object2;
var cntObj = 0;
var selection_object_left = 0;
var selection_object_top = 0;
var src = "http://fabricjs.com/lib/pug.jpg";
fabric.Image.fromURL('https://omicron.aeon.co/images/08e7f2bb-f2ce-4058-a955-1c8d594468a2/card_SIZED-Aleksandr-Zykov-4975950437_b84f9f9ef8_o.jpg', function (oImg) {
oImg.top = canvas.getHeight()/2 - oImg.getHeight()/2;
oImg.left = canvas.getWidth()/2 - oImg.getWidth()/2;
canvas.add(oImg);
bindCropEvent(oImg);
});
canvas.renderAll();
function bindCropEvent(obj){
obj.on('object:dblclick', function(){
CropMode();
});
};
function CropMode() {
canvas.remove(el);
if (canvas.getActiveObject()) {
object = canvas.getActiveObject();
if (lastActive !== object) {
console.log('different object');
} else {
console.log('same object');
}
if (lastActive && lastActive !== object) {
//lastActive.clipTo = null; results in clip loss
}
el = new fabric.Rect({
fill: 'rgba(0,0,0,0.6)',
originX: 'left',
originY: 'top',
stroke: '#ccc',
strokeDashArray: [2, 2],
opacity: 1,
width: 1,
height: 1,
borderColor: 'red',
cornerColor: 'red',
hasRotatingPoint: false
});
el.left = canvas.getActiveObject().left;
selection_object_left = canvas.getActiveObject().left;
selection_object_top = canvas.getActiveObject().top;
el.top = canvas.getActiveObject().top;
el.width = canvas.getActiveObject().width * canvas.getActiveObject().scaleX;
el.height = canvas.getActiveObject().height * canvas.getActiveObject().scaleY;
//插入
canvas.add(el);
canvas.setActiveObject(el);
el.on('deselected', function(){
console.log('des');
doCrop();
});
} else {
alert("Please select an object or layer");
}
}
function doCrop() {
var eLeft = el.get('left');
var eTop = el.get('top');
var left = eLeft - object.left;
var top = eTop - object.top;
console.log(left, top);
left *= 1;
top *= 1;
console.log(left, top);
var eWidth = el.get('width');
var eHeight = el.get('height');
var eScaleX = el.get('scaleX');
var eScaleY = el.get('scaleY');
var width = eWidth * 1;
var height = eHeight * 1;
object.clipTo = function (ctx) {
ctx.rect(-(eWidth / 2) + left, -(eHeight / 2) + top, parseInt(width * eScaleX), parseInt( height * eScaleY));
}
canvas.remove(el);
lastActive = object;
canvas.renderAll();
}
Thanks !
when you create a rect, you can create new image with toDataURL(). What will be cropped image.
cropOptions = {
left: Math.floor(rect.left),
top: Math.floor(rect.top),
width: Math.floor(rect.width),
height: Math.floor(rect.height)
},
cropDataUrl ;
cropDataUrl = image.toDataURL(cropOptions);
new fabric.Image.fromURL(cropDataUrl, function(img) {
canvas.remove(image,rect).add(img); //this is your cropped image
})