Bend Rectangular Image into Square Image around Point [Javascript] - javascript

I have a 360 degree panoramic photo that I want transformed into a cube map.
In order to get the top and bottom faces, I cropped large rectangles from the top 25 percent of the photo and bottom 25% of the photo. The image below represents the bottom 25% of the 360 degree panoramic photo.
and I want to transform it into something like this
using HTML5 and Javascript.
The first image was cropped from a much larger one using this code
imagePieces = [];
numColsToCut = 1;
var numRowsToCut = 1;
var widthOfOnePiece = image.width;
var heightOfOnePiece = image.height / 4;
var startHeight = 0;
var canvas = document.createElement('canvas');
canvas.width = widthOfOnePiece;
canvas.height = heightOfOnePiece;
var context = canvas.getContext('2d');
console.log(image.height / 2);
context.drawImage(image, 0, startHeight, widthOfOnePiece, heightOfOnePiece, 0, 0, canvas.width, canvas.height);
imagePieces.push(canvas.toDataURL());
document.getElementById('myImageElementInTheDom6').src = imagePieces[0];
Before placing the cropped image into the DOM element, I want to 'warp' it into the bottom image. Any tips? :)

Related

How to scale small ImageData to large HTML canvas

I am trying to put image data 100x100 to canvas 1000x1000 , but cant able to do it ,
let width=1000; //canvas width
let height=1000; //canvas height
let img_w=100; //image width
let img_h=100; //image height
let img=new Image();
img.width=img_w
img.height=img_h
img.src="./flower.jpg"
var canvas = document.getElementById('mycanvas');
var context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
let pixels,scannedimg;
img.onload=()=>{
context.drawImage(img, 0, 0,width,height );
scannedimg = context.getImageData(0, 0, img.width, img.height);
pixels=scannedimg.data
console.log(pixels)
redraw();
}
let row=4*img_w;
let col=img_h;
function redraw(){
for(let i=0;i<row;i+=4){
for(let j=0;j<col;j++){
pixels[i+j*row]=0;
pixels[i+j*row+1]=0;
pixels[i+j*row+2]=0;
//pixels[i+j*400+3]=0;
}
}
scannedimg.data=pixels;
console.log(scannedimg);
context.putImageData(scannedimg,0,0,0,0,width,height);
}
i have converted the original array into a black image array (array of zeros) , but while putting on canvas , it is still 100x100
How to scale it to 1000x1000?
i don't want to iterate through 1000x1000 and set it to zero ,
i need a computationally efficient answer
Unless you outsource the pixel calculations to a WebAssembly module a JavaScript-only approach would indeed be rather slow for a large image.
Honestly I'm not sure what you are actually doing in your code.
First your drawing an unknown-sized .jpg to a 1000x1000 canvas which - unless the .jpg is also 1000x1000 - will scale and eventually distort the source image.
let width=1000;
let height=1000;
context.drawImage(img, 0, 0, width, height);
Secondly you're obtaining the pixel data of a 100x100 region from the top-left of your 1000x1000 canvas.
let img_w=100;
let img_h=100;
img.width=img_w;
img.height=img_h;
scannedimg = context.getImageData(0, 0, img.width, img.height);
Finally in your redraw() function you're rather randomly setting some of the pixels to black and draw it back to the canvas at 1000x1000 (which doesn't work that way but I will get into it later).
Let's do it a little different. Say we have a 300x200 image. First we need to draw it to a 100x100 canvas while maintaining it's aspect ratio to get the 100x100 imagedata.
This can be done using a dynamically created off-screen <canvas> element as we don't need to see it.
Now the tricky part is the CanvasRenderingContext2D putImageData() method. I assume you were thinking that the last pair of parameters for the width & height would stretch existing pixel data to fill the region specifid by (x, y, width, height). Well that's not the case. Instead we need to - again - paint the 100x100 pixel data to a same-sized off-screen canvas (or for simlicity re-use the existing) and draw it to the final canvas using the drawImage() method.
Here's everything put together:
let pixelsWidth = 100;
let pixelsHeight = 100;
let finalWidth = 500;
let finalHeight = 500;
let tempCanvas = document.createElement('canvas');
let tempContext = tempCanvas.getContext('2d');
tempCanvas.width = pixelsWidth;
tempCanvas.height = pixelsHeight;
let pixelData;
let img = new Image();
img.crossOrigin = 'anonymous';
img.onload = (e) => {
let scale = e.target.naturalWidth >= e.target.naturalHeight ? pixelsWidth / e.target.naturalWidth : pixelsHeight / e.target.naturalHeight;
let tempWidth = e.target.naturalWidth * scale;
let tempHeight = e.target.naturalHeight * scale;
tempContext.drawImage(e.target, pixelsWidth / 2 - tempWidth / 2, pixelsHeight / 2 - tempHeight / 2, tempWidth, tempHeight);
pixelData = tempContext.getImageData(0, 0, pixelsWidth, pixelsHeight);
redraw();
}
img.src = 'https://picsum.photos/id/237/300/200';
function redraw() {
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
canvas.width = finalWidth;
canvas.height = finalHeight;
tempContext.putImageData(pixelData, 0, 0);
context.drawImage(tempCanvas, 0, 0, finalWidth, finalHeight);
}
canvas {
background: #cccccc;
}
<canvas id="canvas"></canvas>

Wrap image around cylinder using canvas drawImage opacity issue

I need to wrap an image around another image of a mug using javascript, and I found this:
Wrap an image around a cylindrical object in HTML5 / JavaScript
This helps when loading the image that has the mug handle on the left. However when using the same function (with tweaked position values) the image has an opacity applied to it. I searched endlessly to figure out for what reason this is happening however I found nothing :/
This is the function used to wrap the image for the mug with the right handle:
function canvas2() {
var canvas = document.getElementById('canvas2');
var ctx = canvas.getContext('2d');
var productImg = new Image();
productImg.onload = function() {
var iw = productImg.width;
var ih = productImg.height;
canvas.width = iw;
canvas.height = ih;
ctx.drawImage(
productImg,
0,
0,
productImg.width,
productImg.height,
0,
0,
iw,
ih
);
loadUpperIMage();
};
productImg.src =
'https://i.ibb.co/B2G8y1m/white-right-ear.jpg';
function loadUpperIMage() {
var img = new Image();
img.src =
'https://i.ibb.co/BnQP0TL/my-mug-image.png';
img.onload = function() {
var iw = img.width;
var ih = img.height;
var xOffset = 48, //left padding
yOffset = 68; //top padding
var a = 70; //image width
var b = 8; //round ness
var scaleFactor = iw / (6 * a);
// draw vertical slices
for (var X = 0; X < iw; X += 1) {
var y = (b / a) * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
if (!isNaN(y)) {
ctx.drawImage(
img,
X * scaleFactor,
0,
iw / 0.78,
ih,
X + xOffset,
y + yOffset,
1,
162
);
}
}
};
}
}
Hope someone can help with this!
Here is a fiddle with the issue https://jsfiddle.net/L20aj5xr/
It is because of the 4th argument you pass to drawImage - iw / 0.78. By multiplying image width by a value lower than one, you get the value larger than image width. The spec for drawImage says:
When the source rectangle is outside the source image, the source rectangle must be clipped to the source image and the destination rectangle must be clipped in the same proportion.
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
Because the source width (sw) you are using is larger than source image size, the destination rectangle "is clipped in the same proportion". The destination rectangle width is 1px because you chose it as a width for each vertical line you are drawing, and after clipping it's width becomes 1 * 0.78 = 0.78px. The width is now less than 1px and to be honest I am not exactly sure how it actually works under the hood, but my guess is that a browser still needs to draw that 1px, but because the source is 0.78px, it kinda stretches the source to that 1px and adds some anti-aliasing to smooth the transition, which results into added transparency (i.e. browser does not have enough information for that 1px and it tries to fill it up the best it can). You can play around with that by incresing sw even more and observe increasing transparency.
To fix your issue I used the value 20 instead of 0.78 like for the first cup and it seemed to look ok.

How to draw a photo then a shape on top of the photo using a canvas in Ionic?

There are numerous examples out there showing how to draw things onto a canvas, however, my problem is slightly different - I want to load a photo into memory, draw a shape onto exact coordinates over the photo, THEN draw/scale the photo onto a canvas. Not sure where to start with this. Are there any relevant libraries out there I can use with ionic that will allow you to do this?
Edit 1 ~ I now have this mostly working:
private properties:
#ViewChild('mainCanvas') canvasEl: ElementRef;
private _CANVAS: any;
private _CONTEXT: any;
ionViewDidEnter():
this._CANVAS = this.canvasEl.nativeElement;
this._CONTEXT = this._CANVAS.getContext('2d');
updateCanvas():
var img = new Image();
const ctx = this._CONTEXT;
const canvas = this._CANVAS;
ctx.clearRect(0, 0, this._CANVAS.width, this._CANVAS.height);
ctx.fillStyle = "#ff0000";
img.onload = (() => {
img.width = img.width;
img.height = img.height;
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
ctx.lineWidth = 8;
ctx.strokeStyle = "#FF0000";
ctx.strokeRect(100, 100, 400, 400);
ctx.scale(0.5, 0.5); // this does nothing
});
img.src = (<any>window).Ionic.WebView.convertFileSrc(path);
This draws the photo then the rectangle onto the canvas, however, the resulting image is too large to fit onto the screen, so I need to scale the canvas after all drawing is complete. I tried this with ctx.scale but the canvas remains the same size regardless of which values I specify.
You cannot draw straight onto a photo, but what you can do is create an offscreen canvas that is the same size as the photo, draw the photo to it, and then draw your shapes on top.
The result can then be drawn to your main canvas e.g.
// Empty image for example purposes
const img = new Image(100, 100);
// Creating a canvas for example purposes
const mainCanvas = document.createElement('canvas');
const mainCtx = mainCanvas.getContext('2d');
// Create an offscreen buffer
const bufferCanvas = document.createElement('canvas');
const bufferCtx = bufferCanvas.getContext('2d');
// Scale the buffer canvas to match our image
bufferCanvas.width = img.width;
bufferCanvas.height = img.height;
if (bufferCtx && mainCtx) {
// Draw image to canvas
bufferCtx.drawImage(img, 0, 0);
// Draw a rectangle in the center
bufferCtx.fillRect(img.width / 2 - 5, img.height / 2 - 5, 10, 10);
// Draw the buffer to the main canvas
mainCtx.drawImage(bufferCanvas, 0, 0);
}

Resize image and rotate canvas 90 degrees

There's quite a few topics here about rotating images with canvas on js. I read most of them, and couldn't figure out a solution for my problem.
I'm receiving an image (from an upload component) of whatever resolution. I'm resizing it to 1024x768 like:
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
if (img.width >= img.height) {
canvas.width = 1024;
canvas.height = 768;
} else {
canvas.width = 768;
canvas.height = 1024;
}
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
It works fine.
But on Safari/iOs, when I take a picture and upload, the image has ALWAYS a higher width value than height, so the code above doesn't work.
So I decided to use exif-js to detect the image's orientation. When the Orientation attribute is higher than 4, I need to rotate the image 90 degrees, and swap height and width values.
I tried to rotate the image like this:
canvas.width = 768; // swapping values
canvas.height = 1024;
ctx.translate(canvas.width/2, canvas.height/2); // translate to center
ctx.rotate(Math.PI/2); // rotate 90 degrees
ctx.drawImage(img, -img.width/2,-img.height/2); // not sure of the dx and dy values here...
The image is rotated. But it has taken just a small portion of the original image to display on the canvas, so it feels "zoomed in"... it seems that I'm using the wrong values on the drawImage method, but not sure how to fix.
How can I fix this rotation with fixed height and width values?
To rotate 90 deg clockwise on a new canvas.
const canvas = document.createElement("canvas");
canvas.width = image.height;
canvas.height = image.width;
const ctx = canvas.getContext("2d");
ctx.setTransform(
0,1, // x axis down the screen
-1,0, // y axis across the screen from right to left
image.height, // x origin is on the right side of the canvas
0 // y origin is at the top
);
ctx.drawImage(image,0,0);
ctx.setTransform(1,0,0,1,0,0); // restore default
If you need to scale the image to fit a size (assuming image will be rotated)
const width = 1024; // after rotation
const height = 768; // after rotation
const scale = width / image.height; // how much to scale the image to fit
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.setTransform(
0,scale, // x axis down the screen
-scale,0, // y axis across the screen from right to left
width, // x origin is on the right side of the canvas
0 // y origin is at the top
);
ctx.drawImage(image,0,0);
ctx.setTransform(1,0,0,1,0,0); // restore default

blend two images on a javascript canvas

How do you blend two arrays of pixel data to create one image? with the option of using different blending modes?
Pixastic is a special framework for advanced use of canvas, here are blending examples: http://www.pixastic.com/lib/docs/actions/blend/
If you would like do this alone, you can extract pixel data from 2 images, blend it with a mathematical equation, and put into a canvas. Here is information how to get and put pixel data from/to canvas:
http://ajaxian.com/archives/canvas-image-data-optimization-tip
Update:
Simple example with alpha blending of 2 images in proportion 50-50.
(Images borrowed from http://www.pixastic.com/sample/Butterfly.jpg and http://www.pixastic.com/sample/Flower.jpg )
<img src="Butterfly.jpg" id="img1">
<img src="Flower.jpg" id="img2">
<p>Blended image<br><canvas id="canvas"></canvas></p>
<script>
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = img1.width;
var height = img1.height;
canvas.width = width;
canvas.height = height;
var pixels = 4 * width * height;
context.drawImage(img1, 0, 0);
var image1 = context.getImageData(0, 0, width, height);
var imageData1 = image1.data;
context.drawImage(img2, 0, 0);
var image2 = context.getImageData(0, 0, width, height);
var imageData2 = image2.data;
while (pixels--) {
imageData1[pixels] = imageData1[pixels] * 0.5 + imageData2[pixels] * 0.5;
}
image1.data = imageData1;
context.putImageData(image1, 0, 0);
};
</script>
I have created a separate, lightweight, open-source library for perform Photoshop-style blend modes from one HTML Canvas context to another: context-blender. Here's the sample usage:
// Might be an 'offscreen' canvas
var over = someCanvas.getContext('2d');
var under = anotherCanvas.getContext('2d');
over.blendOnto( under, 'screen', {destX:30,destY:15} );
See the README for more information.
I am tasked with recreating this java applet using JavaScript (must be tablet friendly, and work in all modern browsers > IE8).
I am creating images using: var image1 = new Image(); and then setting source: img.src = "some path";
So, from pepkin88 I see that the following function will blend two images by combining their pixel array data, overriding previous data from the first image with the new blended data, and finally putting the new data on the canvas resulting in a blended image:
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = img1.width;
var height = img1.height;
canvas.width = width;
canvas.height = height;
var pixels = 4 * width * height;
context.drawImage(img1, 0, 0);
var image1 = context.getImageData(0, 0, width, height);
var imageData1 = image1.data;
context.drawImage(img2, 0, 0);
var image2 = context.getImageData(0, 0, width, height);
var imageData2 = image2.data;
while (pixels--) {
imageData1[pixels] = imageData1[pixels] * 0.5 + imageData2[pixels] * 0.5;
}
image1.data = imageData1;
context.putImageData(image1, 0, 0); };
HOWEVER, if you viewed the java applet that I'm responsible for recreating, you see that blending happens in real-time continuously as you drag the image around with the pointer the images are constantly blending based on their overlapped regions..
SO, I'm looking to modify the code to account for this, and I continually have the x, y, positions of images drawn (based on top left corner), and the w, h of all images stays static:
the following snippets don't include everything I'm doing, just what I sense is important for you to know
//Rectangle Class from Java converted to JS
function Rectangle(x, y, width, height, src) {
this.x = x;
this.y = y;
this.w = width;
this.h = height;
this.img = new Image();
this.img.src = src;
}
//Stores instance in rect array
rect[0] = new Rectangle(1, (height - 111)/2, 150, 105, "images/mMain.png");
//Draw method that's called
Rectangle.prototype.draw = function(ctx) {
//this.checkBound();
ctx.drawImage(this.img, this.x, this.y, this.w, this.h);
prepareMix(this.img, this.x, this.y, this.w, this.h);
}
So, I'm working on a prepareMix function that receives image info and uses it to get and store image data:
function prepareMix(src, x, y, w, h) {
pixels = 4 * w * h;
var image = mtx.getImageData(x, y, w, h);
var imgData = image.data;
}
Made a list of what to do:
Sense the overlapping
Get and Store the overlapping image data
Mix the overlapping region data arrays
Replace the overlapping image data with the blended data
Put the new data on the canvas
1. Sense the Overlapping:
Plan: Store image positions and compare positions data to know whether or not overlapping is occurring.
IF overlapping is TRUE, which two images is it true for? Distinguish these images that're overlapping from other images so that methods can be called on them.
js, css, html, and images in zip here BOX

Categories