I wish to present a video, frame by frame with the possibility of flipping between frames in google colab. I also wish to mark some rectangles and get the x,y,w,h of each rectangle.
As far as I could understand, the only way to click the output and gather data in google Collab is via a javascript call, which I am new to. I have split my video into a list called frames and written the following python\javascript code for my task:
def get_fxy_val(base64imgs):
js = Javascript('''
async function showImage(base64) {
var canvas = document.createElement('canvas');
var rect = {};
var drag = false;
var finish = false;
var clicks = [];
var img = document.createElement('img');
var frame = 0
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
img = new Image();
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
};
img.src = 'data:image/jpeg;base64,'+base64[frame];
var prev = document.createElement('button');
prev.innerHTML = '<';
prev.onclick = function() {
frame = frame -1;
if(frame<0){
frame = 0;
}
else{
// console.log(frame)
img.src = 'data:image/jpeg;base64,'+base64[frame];
};
};
document.body.appendChild(prev);
var next = document.createElement('button');
next.innerHTML = '>';
next.onclick = function() {
frame = frame+1;
if (frame>base64.length-1) {
// console.log(frame)
frame = base64.length-1
}
else{
img.src = 'data:image/jpeg;base64,'+base64[frame];
}
};
document.body.appendChild(next);
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
function mouseDown(e) {
rect.startX = e.pageX - this.offsetLeft;
rect.startY = e.pageY - this.offsetTop;
drag = true;
}
function mouseUp() {
drag = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
if (rect.w<0){
rect.startX = rect.startX + rect.w
rect.w = rect.w*(-1)
}
if (rect.h<0){
rect.startY = rect.startY + rect.h
rect.h = rect.h*(-1)
}
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
}
function mouseMove(e) {
if (drag) {
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
}
}
var saver = document.createElement('button');
saver.innerHTML = 'save rectangle';
saver.onclick = function() {
clicks.push([frame, rect.startX, rect.startY, rect.w, rect.h]);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
};
document.body.appendChild(saver);
var finisher = document.createElement('button');
finisher.innerHTML = 'Done!';
finisher.onclick = function() {
};
document.body.appendChild(finisher);
await new Promise((resolve) => finisher.onclick = resolve);
document.body.removeChild(finisher)
document.body.removeChild(saver)
document.body.removeChild(next)
document.body.removeChild(prev)
document.body.removeChild(canvas)
return clicks
}
''')
display(js)
imgs = ["'"+b64img+"'" for b64img in base64imgs]
imgs = '[' + ','.join(imgs) +']'
data = eval_js(f"showImage({imgs})")
return data
I call it using:
base64imgs = [base64.b64encode(cv2.imencode('.jpg', img)[1]).decode() for img in frames]
data = get_fxy_val(base64imgs)
Everything works well for a small number of images, however for the full video Collab crushes and I cannot manage to find any logs to verify what is going on.
Two thoughts I had, which I do not know how to implement:
Reduce the images' memory size somehow. I cannot harm the aspect ratio so downsampling is not a possibility.
Load images directly from memory inside the javascript code. I have no idea how to implement this or if it is even possible.
Can I implement any of these? Is there another approach?
B.T.W, my video has images with size h,w,c = (1080, 1920, 3).
Related
I'm trying to upload an image to canvas and draw squares on it and get the squares' coordinates to send it to the backend for keys identification.
But something not right, I can't draw squares on my images.
This is my javascript which I use:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var imageObj = null;
function init() {
imageObj = new Image();
imageObj.onload = function () { ctx.drawImage(imageObj, 0, 0); };
imageObj.src =
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
}
function mouseDown(e) {
rect.startX = e.pageX - this.offsetLeft;
rect.startY = e.pageY - this.offsetTop;
drag = true;
}
function mouseUp() { drag = false; }
function mouseMove(e) {
if (drag) {
ctx.clearRect(0, 0, 500, 500);
ctx.drawImage(imageObj, 0, 0);
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
ctx.strokeStyle = 'red';
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
}
}
//
init();
</script>
<script type="text/javascript">
document.getElementById('imageLoader').onchange = function(e) {
var img = new Image();
img.onload = draw;
img.onerror = failed;
img.src = URL.createObjectURL(this.files[0]);
};
function draw() {
var canvas = document.getElementById('canvas');
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(this, 0,0);
}
function failed() {
console.error("The provided file couldn't be loaded as an Image media");
}
and HTML:
<canvas id="canvas" ></canvas>
<input type="file" id="imageLoader" name="imageLoader"/>
You just need to assign the image to imageObj.
function draw() {
canvas.width = this.width;
canvas.height = this.height;
ctx.drawImage(this, 0,0);
imageObj = this; //here
}
JSFiddle here
I'm trying to blur a part of the photo. My code is as follows:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var imageObj = null;
function init() {
imageObj = new Image();
imageObj.onload = function () { ctx.drawImage(imageObj, 0, 0); };
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
}
function mouseDown(e) {
rect.startX = e.pageX - this.offsetLeft;
rect.startY = e.pageY - this.offsetTop;
drag = true;
}
function mouseUp() { drag = false; }
function mouseMove(e) {
if (drag) {
ctx.clearRect(0, 0, 500, 500);
ctx.drawImage(imageObj, 0, 0);
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
ctx.strokeStyle = 'blue';
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
ctx.filter = 'blur(5px)';
}
}
//
init();
<canvas id="canvas" width="500" height="500"></canvas>
I draw a rectangle but I want to apply the blur filter only on that rectangle not to the whole image as it is now. Any idea how to do that?
Here is the fiddle
It is possible by using HTML5 Canvas
I have made a fiddle to blur the part 350 from image.
Fiddle Link: https://jsfiddle.net/k6aaqdx6/3/
Edit:
updated according to your fiddle: https://jsfiddle.net/tbjLk6eu/2
Code that I added:
imgData=ctx.getImageData(rect.startX, rect.startY, rect.w, rect.h);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.filter = 'none';
ctx.drawImage(imageObj, 0, 0);
ctx.putImageData(imgData,rw, rh);
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
working jsfiddle: https://jsfiddle.net/zoh5o9p5/
If you use a base64 image and do some changes it will work as you expected
made some changes in mouseMove function.
function mouseMove(e) {
if (drag) {
ctx.filter = 'blur(5px)';
ctx.drawImage(imageObj, 0, 0);
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
ctx.strokeStyle = 'blue';
if(rect.w>0 && rect.h>0)
{
imgDrow=ctx.getImageData(rect.startX, rect.startY, rect.w, rect.h);
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.filter = 'none';
ctx.drawImage(imageObj, 0, 0);
w=rect.w<0?rect.startX+rect.w:rect.startX;
h=rect.h<0?rect.startY+rect.h:rect.startY;
if(imgDrow)
{
ctx.putImageData(imgDrow,w, h);
}
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
}
working jsfiddle: https://jsfiddle.net/zoh5o9p5/
we can use fabric.js library. Here you can draw a rectangle and move it and resize it. The original fiddle was having some console error and scaling was not working. I have resolved them all in the bellow jsfiddle.
Here we first copy the original image and create a copy canvas and make the full image blur. Then on move or scaling of the rectangle just showing actual crop to show the proper blur section.
function blurSelection(left=0, top=0, img=null) {
if (img) {
img.cropX = left;
img.cropY = top;
fabricCanvas.renderAll();
} else {
const image = fabric.Image.fromURL(blurredCanvas.toDataURL(), function (img) {
img.cropX = left;
img.cropY = top;
img.width = 200; // Default
img.height = 100; // Default
img.objectCaching = false;
fabricCanvas.add(img);
img.bringToFront();
img.on('moving', function (options) {
const newLeft = options.target.left;
const newTop = options.target.top;
blurSelection(newLeft, newTop, img=options.target);
});
img.on('scaling', function (options) {
const newLeft = options.target.left;
const newTop = options.target.top;
const newWidth = options.target.width * options.target.scaleX;
const newHeight = options.target.height * options.target.scaleY;
//console.log("scaleX",options.target.scaleX,'scaleY',options.target.scaleX,"newWidth",newWidth,"newHeight",newHeight)
options.target.scaleX=1;
options.target.scaleY=1;
options.target.width=newWidth;
options.target.height=newHeight;
})
});
}
}
function copyCanvas() {
const objects = fabricCanvas.getObjects();
const copiedCanvas = fabricCanvas.toCanvasElement();
const blurredImage = new fabric.Image(copiedCanvas);
const filter = new fabric.Image.filters.Blur({
blur: 0.8
});
blurredImage.filters.push(filter);
blurredImage.applyFilters();
blurredCanvas = new fabric.Canvas(copiedCanvas);
window.blurredCanvas = blurredCanvas;
blurredCanvas.add(blurredImage);
blurredCanvas.renderAll();
// Just for the inspection of the blurred image
document.getElementById('asd').src = copiedCanvas.toDataURL();
}
https://jsfiddle.net/debchy/ajbpefk4/7/
I have a function that clears all drawings off of the background image in canvas when a button is clicked, but when you go to draw again, the old (previously cleared drawings) reappears. How can I make a hard delete so that I can draw again without reloading the page
'use strict';
function initCanvas() {
let bMouseIsDown = false;
let canvas = document.getElementById('cvs');
let ctx = canvas.getContext('2d');
let convert = document.getElementById('convert');
let sel = 'png';
let imgs = document.getElementById('imgs');
let imgW = 300;
let imgH = 200;
let background = new Image();
background.crossOrigin = '';
background.src = "http://i.imgur.com/yf6d9SX.jpg";
background.onload = function(){
ctx.drawImage(background,0,0,600,400);
}
bind(canvas,ctx,convert,sel,imgs,imgW,imgH,bMouseIsDown);
};
initCanvas()
function bind (canvas,ctx,convert,sel,imgs,imgW,imgH,bMouseIsDown) {
let iLastX = 0;
let iLastY = 0;
let iX;
let iY;
canvas.onmousedown = function(e) {
bMouseIsDown = true;
iLastX = e.clientX - canvas.offsetLeft + (window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft);
iLastY = e.clientY - canvas.offsetTop + (window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop);
}
canvas.onmouseup = function() {
bMouseIsDown = false;
iLastX = -1;
iLastY = -1;
}
canvas.onmousemove = function(e) {
if (bMouseIsDown) {
iX = e.clientX - canvas.offsetLeft + (window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft);
iY = e.clientY - canvas.offsetTop + (window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop);
ctx.moveTo(iLastX, iLastY);
ctx.lineTo(iX, iY);
ctx.stroke();
ctx.strokeStyle = "blue";
ctx.lineJoin = "round";
ctx.lineWidth = 5;
iLastX = iX;
iLastY = iY;
}
};
document.getElementById('clear').addEventListener('click', function() {
rerenderImg();
});
function rerenderImg() {
iY = [];
iX=[];
initCanvas()
}
};
You need to call ctx.beginPath(); before drawing on the canvas again.
The logical place to put this is just before your call to ctx.moveTo.
An explanation of why this is needed is given here.
You should actually run clearRect on the canvas:
ctx.clearRect(0, 0, canvas.width, canvas.height);
I need to add a touch feature to the canvas app that I'm working on (multiple users white board), and I already read about the event listeners and the event.preventDefault(), but I can't understand how can I use two event listeners with 'DOMContentLoaded' and 'touchmove'. At this point I don't know if using multiple event listeners is the solution I need.
This is the code I'm using:
document.addEventListener("DOMContentLoaded", function() {
var mouse = {
click: false,
move: false,
pos: {x:0, y:0},
pos_prev: false
};
var canvas = document.getElementById('drawing');
var context = canvas.getContext('2d');
var width = 1280;
var height = 960;
var socket = io.connect();
var lineWidth = 1;
var shadowBlur = 1;
var shadowColor = "black";
var strokeStyle = "black";
canvas.width = width;
canvas.height = height;
canvas.onmousedown = function(e){ mouse.click = true; };
canvas.onmouseup = function(e){ mouse.click = false; };
canvas.onmousemove = function(e) {
mouse.pos.x = e.clientX / width;
mouse.pos.y = e.clientY / height;
mouse.move = true;
};
socket.on('draw_line', function (data) {
var line = data.line;
context.beginPath();
context.lineWidth = lineWidth;
context.shadowBlur = shadowBlur;
context.strokeStyle = strokeStyle;
context.lineJoin="round";
context.lineCap = "round";
context.moveTo(line[0].x * width, line[0].y * height);
context.lineTo(line[1].x * width, line[1].y * height);
context.stroke();
});
function mainLoop() {
if (mouse.click && mouse.move && mouse.pos_prev) {
socket.emit('draw_line', { line: [ mouse.pos, mouse.pos_prev ] });
mouse.move = false;
}
mouse.pos_prev = {x: mouse.pos.x, y: mouse.pos.y};
setTimeout(mainLoop, 50);
}
mainLoop();
});
I would really appreciate any help.
Thanks.
Are you encountering some sort of bug? Right now it appears that your code operates by listening for the mouse being clicked and the mouse being moved. If the mouse is pressed down, the mouse is being moved and the loop has run at least once (mouse.click && mouse.move && mouse.pos_prev) then it draws a line on the canvas element.
When i call writeTextToCanvas method before clearCanvas method, it works perfectly,
if i call clearCanvas method first than writeTextToCanvas, it doesn't works, drawing functions etc. all works after clearCanvas but fillText doesn't work and also clears all canvas when i call fillText
when i set context.globalAlpha = 0.5 before fillText, i can barely see text on the canvas but anything in canvas erased somehow
var canvas;
var context;
var selectedRole = DRAW_TOOLS.PENCIL;
function initDrawingCanvas(index) {
var question = document.getElementsByClassName('question').item(index);
canvas = question.getElementsByClassName('questionDrawingCanvas').item(0);
context = canvas.getContext("2d");
canvasWidth = canvas.width;
canvasHeight = canvas.height;
compositeOperation = context.globalCompositeOperation;
canvas.addEventListener("touchmove", onCanvasTouchMove, false);
canvas.addEventListener("touchstart", onCanvasTouchStart, false);
canvas.addEventListener("touchstop", onCanvasTouchStop, false);
}
var startX = 0;
var startY = 0;
var endX = 0;
var endY = 0;
var compositeOperation;
function onCanvasTouchMove(event){
endX = event.changedTouches[0].clientX - $(canvas).offset().left;
endY = event.changedTouches[0].clientY - $(canvas).offset().top;
if(selectedRole == DRAW_TOOLS.PENCIL){
context.beginPath();
context.moveTo(startX, startY);
context.lineTo(endX, endY);
context.strokeStyle = $('.colorPicker').css('background-color');
context.lineWidth = parseInt($('#pencilSize').attr("data-size"));
context.stroke();
}else if(selectedRole == DRAW_TOOLS.ERASER){
context.save();
context.globalCompositeOperation = "destination-out";
context.arc(endX,endY,25,0,Math.PI*2,false);
context.fill();
context.restore();
}
startX = endX;
startY = endY;
}
function onCanvasTouchStart(event){
$("#colorPicker").css("display", "none");
$("#pencilSize").css("display", "none");
$("#textColorPicker").css("display", "none");
$("#canvas_textSize").css("display", "none");
startX = event.changedTouches[0].pageX - $(canvas).offset().left;
startY = event.changedTouches[0].pageY - $(canvas).offset().top;
if(selectedRole == DRAW_TOOLS.TEXT){
showConfirmDialog('<textarea id="canvasTextArea" class="canvasTextArea"></textarea>', RESOURCES.CANVAS_TEXT_TITLE, {positiveButton: RESOURCES.OKAY, negativeButton: RESOURCES.CANCEL}, function(){
var text = $("#canvasTextArea").val();
if(isNullOrUndefined(text)) return;
writeTextToCanvas(text);
hideDialog();
}, function(){
hideDialog();
});
$("#canvasTextArea").focus();
$("#canvasTextArea").css("font-size", $('#textSizePicker').attr("data-size") + "px");
$("#canvasTextArea").css("color", $('.textColorPicker').css("background-color"));
}
}
function onCanvasTouchStop(event){
console.log(event);
}
function clearCanvas(){
context.clearRect(0, 0, canvas.width, canvas.height);
}
function getCanvasContent(){
return canvas.toDataURL();
}
function writeTextToCanvas(text) {
context.globalCompositeOperation = "source-over";
context.textBaseline = "top";
context.font = $('#textSizePicker').attr("data-size") + 'px sans-serif';
context.fillStyle = $('.textColorPicker').css("background-color");
context.fillText(text, startX, startY);
}
What should i use to clear canvas or clear some parts like eraser then i can fillText normally?
Your call to clearRect() is stopping the entire script because it throws an error (check the console). You're not passing in a rectangle to the call.
It needs to be called like:
context.clearRect(0, 0, canvas.width, canvas.height);
Here's a working example: http://jsfiddle.net/pe19L15m