Jcrop Image Intervention Laravel 5 - image

i am using Image Intervention and jcrop to crop and resize image in laravel, but having problems. The issue which i think is that , when i save the file width and height is correct according to the selection, but the x & y is not correct, I am completely lost here, dont know what to do , Please help.
I have made but cropping area is wrong.
here is the code example.
// convert bytes into friendly format
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB'];
if (bytes == 0) return 'n/a';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
}
// check for selected crop region
function checkForm() {
if (parseInt($('#w').val())) return true;
$('.setting-image-error').html('Select area').show();
return false;
}
// update info by cropping (onChange and onSelect events handler)
function updateInfo(e) {
$('#x1').val(e.x);
$('#y1').val(e.y);
$('#x2').val(e.x2);
$('#y2').val(e.y2);
$('#w').val(e.w);
$('#h').val(e.h);
}
// clear info by cropping (onRelease event handler)
function clearInfo() {
$('#w').val('');
$('#h').val('');
}
// Create variables (in this scope) to hold the Jcrop API and image size
var jcrop_api, boundx, boundy;
function fileSelectHandler() {
// get selected file
var oFile = $('#picture')[0].files[0];
// hide all errors
$('.setting-image-error').hide();
// check for image type (jpg and png are allowed)
var rFilter = /^(image\/jpeg|image\/png)$/i;
if (!rFilter.test(oFile.type)) {
$('.setting-image-error').html('Select only jpg, png').show();
return;
}
// check for file size
if (oFile.size > 10000000) {
$('.setting-image-error').html('Too Big file ').show();
return;
}
// preview element
var oImage = document.getElementById('preview');
// prepare HTML5 FileReader
var oReader = new FileReader();
oReader.onload = function (e) {
// e.target.result contains the DataURL which we can use as a source of the image
oImage.src = e.target.result;
oImage.onload = function () { // onload event handler
// display step 2
$('.setting-image-cropping-stage').fadeIn(500);
// display some basic image info
var sResultFileSize = bytesToSize(oFile.size);
$('#filesize').val(sResultFileSize);
$('#filetype').val(oFile.type);
$('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);
// destroy Jcrop api if already initialized
if (typeof jcrop_api !== 'undefined') {
jcrop_api.destroy();
jcrop_api = null;
$('#preview').width(oImage.naturalWidth);
$('#preview').height(oImage.naturalHeight);
}
//Scroll the page to the cropping image div
$("html, body").animate({scrollTop: $(document).height()}, "slow");
// initialize Jcrop
$('#preview').Jcrop({
minSize: [32, 32], // min crop size
aspectRatio: 1, // keep aspect ratio 1:1
bgFade: true, // use fade effect
bgOpacity: .3, // fade opacity
onChange: updateInfo,
onSelect: updateInfo,
onRelease: clearInfo
}, function () {
// use the Jcrop API to get the real image size
var bounds = this.getBounds();
boundx = bounds[0];
boundy = bounds[1];
// Store the Jcrop API in the jcrop_api variable
jcrop_api = this;
});
}
}
// read selected file as DataURL
oReader.readAsDataURL(oFile);
}
and Controller code is below.
public function image_crop_resize_and_upload($file, $user_id,$width,$height,$x1,$y1)
{
$filename = $user_id . '.jpg';// image file name
$target_path = User::PICTURE_PATH . $filename;//path where to create picture with new dimensions
$img = \Image::make($file->getRealPath());// create the instance of image with the real path of the image
$filetype = $img->mime();//get file mime type
$filetypes = ['image/jpg', 'image/jpeg', 'image/png']; //allowed files types
//if file exists in the target folder, system will delete the file and next step will create new one.
if (File::exists($target_path)) {
File::delete($target_path);
}
if (in_array($filetype, $filetypes, true)) {
$img->crop($width, $height,$x1,$y1);
$img->encode('jpg', 85);
$img->resize($width,$height);
$img->save('uploads/' . $user_id . '.jpg');
return true;
} else {
return false;
}
}
When i have the file the file width and height is correct, but the selection area, x & y is not correct.

Yes , I have got the answer. The problem is very simple the x and y position of image are wrong because it is inside a bootstrap responsive class. the proper solution is just to remove the class . So the image actually dimension will be shown. and than select the are. Thats it.
<img id="preview" name="preview" class="img-responsive"/>
this should be
<img id="preview" name="preview"/>

Related

Converting image to jpg Angular 4

I am using Angular 4 with TypeScript for a web application. I am allowing users to upload a thumbnail profile photo from their device as either a png, jpeg, or jpg, and I want to convert that photo to a jpg on the frontend. I am looking for some way to do this, as the file type is readonly.
The reason that I am doing this is so that when users load the profile page, they don't have to download large images so the page is quick. I think that converting to a jpg might be the best bet because when tested with a sample image, a png of an image was 35.4KB while a converted jpg of the same image was 6.7KB. If there is a better solution, I would love to hear it!
Thank you in advance!
I just wrote some code like your requirements, here is what I did:
load a local image by FileReader and add a listener to its onload event
in the onload listener, create an "Image" object by new Image(), and set "src" attribute by the loaded image "src" (Data URL format)
create a "Canvas" element, and draw the image on this canvas
use Canvas.toDataURL() to fetch the converted image data (in base64)
post the image data to server
After you draw an image to canvas, call Canvas.toDataURL() will get the canvas content in Data URL string, please note that it's the canvas data not original image data, for example, if image size is 100 x 100 and canvas size is 50 x 50, you'll get an image in 50 x 50 pixel with this function, so if you want a full size image, you need to resize the canvas to the certain size.
this function has two parameters:
canvas.toDataURL(type, encoderOptions);
type - A DOMString indicating the image format. The default format type is image/png. in your code, set to "image/jpeg" in your case
encoderOptions - A Number between 0 and 1 indicating image quality if the requested type is image/jpeg or image/webp.
Here is my "preview and upload" function write in TypesScript for reference:
preview(input: HTMLInputElement) {
if (input.files.length) {
let reader = new FileReader();
reader.onload = (e) => {
if (!this.img) {
this.img = new Image();
}
this.img.src = (e.target as any).result;
this.img.onload = () => {
// omit code of setting 'dx', 'dy', 'width', 'height'
let ctx = <CanvasRenderingContext2D>this.canvas.nativeElement.getContext('2d');
ctx.drawImage(this.img, dx, dy, width, height);
let dataUrl = (<HTMLCanvasElement>this.canvas.nativeElement).toDataURL('image/jpeg', 0.7);
this.uploadService.upload(dataUrl).then((resp: any) => {
if (resp.key) {
this.asset.image = resp.key;
}
});
};
}
reader.readAsDataURL(input.files[0]);
}
}
I omitted some variables: dx, dx, width, height in the above code, I use these variables to adjust image position (for clipping purpose).
This is JavaScript example:
function _elm(id) {
return document.getElementById(id);
}
_elm('fileInput').onchange= function(event) {
if (event.target.files.length) {
var fileReader = new FileReader();
fileReader.onload = function(event) {
var img = new Image();
img.src = event.target.result;
_elm('sizeRaw').innerText = '+ data-url size ' + event.target.result.length;
img.onload = function() {
var canvas = _elm('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, 200, 200);
var dataUrl = canvas.toDataURL('image/jpeg', 0.5);
_elm('output').innerText = dataUrl;
_elm('sizeNew').innerText = '+ data-url size ' + dataUrl.length;
}
};
fileReader.readAsDataURL(event.target.files[0]);
}
};
#canvas {
border: 1px solid blue;
}
#output {
word-break: break-all;
}
<h3>Input file <span id="sizeRaw"></span>: </h3>
<input id="fileInput" type="file" accept="image/*">
<h3>Canvas</h3>
<div>
<canvas id="canvas" width="200" height="200"></canvas>
</div>
<h3>Output <span id="sizeNew"></span>: </h3>
<div id="output">
</div>

Clicking or hovering over multiple images in canvas?

I am working on a project for my programming class. I'd like to essentially have a canvas with a background element (say a room.jpg) and then maybe three interactive objects in the room (lamp.jpg, couch.jpg, desk.jpg). I'd like for it to be that if you hover over the lamp a small box or text pops out, giving you some information. Or maybe have it so if you click an image, the same concept happens. You know, something interactive with the objects in the canvas. Again, I'm new to canvas but we have to use it in our assignment. My current code is:
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var sources = {
room: 'room.jpg',
title: 'title.jpg'
};
loadImages(sources, function(images) {
context.drawImage(images.room, 0,0);
context.drawImage(images.title, 0,0);
});
}
But from what I understand, it makes the two jpegs a "permanent" canvas element (unable to be messed with). I had been trying to get it so that when I clicked I'd go from the title.jpg to the room.jpg but I've since given up. Essentially, all I want now is just to have the room.jpg appear when the page is first loaded, and have three other png objects on top (as objects in the room). Are these able to be interacted with, or do I have to put the images into the canvas in another way? Thanks for all your help and your patience!
// --- Image Loader ----
var images = {};
var loadedImages = 0;
var pictures = {
room: 'room.jpg',
title: 'title.jpg'
lamp1: 'lampoff.jpg'
lamp2: 'lampon.jpg'
};
function loadImages(sources, callback) {
var numImages = 0;
for(var src in sources)numImages++;
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
// --- Mouse Down Functionality ----
$('#canvas').addEventListener('mouseDown', function(e){
if(e.clientX){
var rect = this.getBoundingClientRect();
if(rect) clickCanvas(e.clientX - rect.left, e.clientY - rect.top)
else clickCanvas(e.clientX - this.offsetLeft, e.clientY - this.offsetTop);
}else if(e.offsetX) clickCanvas(e.offsetX, e.offsetY);
else if(e.layerX) clickCanvas(e.layerX, e.layerY);
else console.warn("Couldn't Determine Mouse Coordinates");
})
var lampOn;
function drawCanvas(showLamp){
lampOn = showLamp;
canvas.width = canvas.width //clears canvas
context.drawImage(images.room, 0,0);
context.drawImage(images.title, 0,0);
if(lampOn){
context.drawImage(images.lamp2, 100,100);
}else{
context.drawImage(images.lamp1, 100,100);
}
}
function clickCanvas(x,y){
console.log('clicked canvas at:',x,y)
if(clickedLamp){
drawCanvas(!lampOn)
}
}
loadImages(pictures, function(images) {
drawCanvas(false)
});
Make sure to replace "clickedLamp" and "#canvas"! The idea here is that you redraw the same canvas, using the same function. Everytime you modify any of it, you rerender ALL of it. See if you can get this example working, it will help clarify alot. If you don't understand something comment

Have embedded images open with FancyBox, not in new window

This section of code in my /js/global.js file activate each image to open in a new window when clicked. Is it possible to alter this code to have each open in a FancyBox instead? I have downloaded a FancyBox plugin for a Vanilla forum I am running, and it currently only targets images embedded in posts After You Click On The Post Itself. On the main page, clicking on an image opens a new window.
// Shrink large images to fit into message space, and pop into new window when clicked.
// This needs to happen in onload because otherwise the image sizes are not yet known.
jQuery(window).load(function() {
var props = ['Width', 'Height'], prop;
while (prop = props.pop()) {
(function (natural, prop) {
jQuery.fn[natural] = (natural in new Image()) ?
function () {
return this[0][natural];
} :
function () {
var
node = this[0],
img,
value;
if (node.tagName.toLowerCase() === 'img') {
img = new Image();
img.src = node.src,
value = img[prop];
}
return value;
};
}('natural' + prop, prop.toLowerCase()));
}
jQuery('div.Message img').each(function(i,img) {
var img = jQuery(img);
var container = img.closest('div.Message');
if (img.naturalWidth() > container.width() && container.width() > 0) {
img.wrap('');
}
});
// Let the world know we're done here
jQuery(window).trigger('ImagesResized');
});
Add an specific class to your wrapped images, modifying this line
img.wrap('');
... into this :
img.wrap('<a class="fancybox" href="'+$(img).attr('src')+'"></a>');
Then bind fancybox to that selector (".fancybox") in a custom script like :
$(".fancybox").fancybox();
This assumes that you have properly loaded the fancybox js and css files.

Merge 2 images with node.js?

I want to merge 2 images using node.js. Or rather, i want to place one smaller image on cordinates x,y on a larger image.
Even more precise: I have an image of glasses, and an image of a face and i want to put the glasses on the face.
I did some googling, and found some image manipulating libraries, but none seem to be able to merge images.
You might need this: https://github.com/zhangyuanwei/node-images
Cross-platform image decoder(png/jpeg/gif) and encoder(png/jpeg) for Nodejs
images("big.jpg").draw(images("small.jpg"), 10, 10).save("output.jpg");
I do no have enough reputation to add a comment, or else I would to Schmidko's answer.
Sharp works well, however, overlayWith is deprecated and you instead need to use composite. See below:
sharp(path + 'example.jpg')
.composite([{input: path + 'logo.png', gravity: 'southeast' }])
.toFile(path + 'output.png');
If you would like to center the image being overlayed:
gravity: 'centre'
I've used:
https://github.com/learnboost/node-canvas
to do something similar (build a composite image from components on the fly).
It works great.
Here's some example code:
var Canvas = require('canvas'),
fs = require('fs'),
Image = Canvas.Image;
var _components = [{prefix:'f', count:12},
{prefix:'h', count:12},
{prefix:'i', count:12},
{prefix:'m', count:12}];
var _total = 1;
for (var i=_components.length - 1; i>=0; i--){
_components[i].mult = _total;
_total *= _components[i].count;
}
module.exports.ensureImageExists = function(img, cb){
fs.stat(__dirname + '/../public/images/rb/' + img, function(err, stats){
if (err){
if (err.code == 'ENOENT')
generateImage(img, cb);
else
cb(err);
}
else{
cb();
}
});
}
function generateImage(name, cb){
var re = /rb([0-9]*)\.png/
var num = parseInt(re.exec(name)[1]) % _total;
var parts = [];
for (var i=0; i<_components.length; i++){
var n = Math.floor(num / _components[i].mult);
parts.push(_components[i].prefix + (n + 1));
num -= n * _components[i].mult;
}
var canvas = new Canvas(45, 45),
ctx = canvas.getContext('2d');
drawParts();
function drawParts(){
var part = parts.shift();
if (!part)
saveCanvas();
else {
var img = new Image;
img.onload = function(){
ctx.drawImage(img, 0, 0, 45, 45);
drawParts();
};
img.src = __dirname + '/components/' + part + '.png';
}
}
function saveCanvas(){
canvas.toBuffer(function(err, buf){
if (err)
cb(err);
else
fs.writeFile(__dirname + '/../public/images/rb/' + name, buf, function(){
cb();
});
});
}
}
In this case, the components are selected based upon the name of the image, but you clearly could do otherwise. Also, I imagine you could just stream the image out if you wanted -- I write it to a file so it's available the next time it's requested.
I put a route like this in to handle the generation:
app.get('/images/rb/:img', requireLogin, function(req, res, next){
//first make sure image exists, then pass along so it is handled
//by the static router
rbgen.ensureImageExists(req.params.img, function(err){
next();
})
});
Tested some librarys for a similar task and implemented finally this one.
https://github.com/lovell/sharp.
Clean API and all you need to merge two images together.
Example:
sharp(path + 'example.jpg')
.overlayWith(path + 'label.png', { gravity: sharp.gravity.southeast } )
.toFile(path + 'output.png')

detecting "we have no imagery" of google maps street view static images

I'm generating street view static images like so:
https://maps.googleapis.com/maps/api/streetview?size=1080x400&location=%s&fov=90&heading=235&pitch=0&key=%s
If you visit that link you see an image that says, "Sorry, we have no imagery for this..."
Is there any way to detect this "sorry" state so that I can fall back to another image?
One quick solution would be to load the image file using xmlrpc and check that its md5sum is 30234b543d5438e0a0614bf07f1ebd25, or that its size is 1717 bytes (it's unlikely that another image can have exactly the same size), but that's not very robust since I have seen Google change the position of the text in the image. Though it's a very good start for a prototype.
You could go for image processing instead. Note that it's still not perfectly robust since Google could decide to change the looks of the image anytime. You'll have to decide whether it's worth it.
Anyway, here is how I would do it using jQuery:
load the image and open a 2D context for direct pxiel access (see this question for how to do it)
analyse the image:
sample groups of 2×2 pixels at random locations; I recommend at least 30 groups
a group of 2×2 pixels is good if all the pixels have the same value and their R/G/B values do not differ by more than 10% (ie. they're grey)
count the ratio of good pixel groups in the image
if there are more than 70% good pixel groups, then we are pretty sure this is the “no imagery” version: replace it with another image of your choice.
The reason I do not recommend testing directly for an RGB value is because JPEG decompression may have slightly different behaviours on different browsers.
this situation is already build in in the 3.0 version due
the boolean test status === streetviewStatus.Ok, here is a snippet from my situation solving
if (status === google.maps.StreetViewStatus.OK) {
var img = document.createElement("IMG");
img.src = 'http://maps.googleapis.com/maps/api/streetview?size=160x205&location='+ lat +','+ lng +'&sensor=false&key=AIzaSyC_OXsfB8-03ZXcslwOiN9EXSLZgwRy94s';
var oldImg = document.getElementById('streetViewImage');
document.getElementById('streetViewContainerShow').replaceChild(img, streetViewImage);
} else {
var img = document.createElement("IMG");
img.src = '../../images/ProfilnoProfilPicture.jpg';
img.height = 205;
img.width = 160;
var oldImg = document.getElementById('streetViewImage');
document.getElementById('streetViewContainerShow').replaceChild(img, streetViewImage);
}
As of 2016, you can use the new Street View Image Metadata API.
Now you just need the status field to know if a panorama is found.
Example requests:
https://maps.googleapis.com/maps/api/streetview/metadata?size=600x300&location=78.648401,14.194336&fov=90&heading=235&pitch=10&key=YOUR_API_KEY
{
"status" : "ZERO_RESULTS"
}
https://maps.googleapis.com/maps/api/streetview/metadata?size=600x300&location=eiffel%20tower,%20paris,%20france&heading=-45&pitch=42&fov=110&key=YOUR_API_KEY
{
...
"status" : "OK"
}
You can use the getPanoramaByLocation function (see http://code.google.com/apis/maps/documentation/javascript/services.html#StreetViewService).
try something like this:
function handleMapClick()
{
var ll= new google.maps.LatLng(latitude,longitude);
sv.getPanoramaByLocation(ll, 50, processSVData);
}
function processSVData(data, status) {
if (status==google.maps.StreetViewStatus.ZERO_RESULTS)
{
<DO SOMETHING>
}
}
Request a google street view image and if it has a specific file size it is a 'Not street view avaible'. I did the follwing:
var url = 'google street view url';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function (e) {
if (this.status == 200) {
try {
var image = new Blob([this.response], {type: 'image/jpeg'});
if (image.size) {
if (url.indexOf('640x640') > -1 && image.size === 8410) {
// Not street view
}
if (url.indexOf('400x300') > -1 && image.size === 3946) {
// Not street view
}
}
} catch (err) {
// IE 9 doesn't support blob
}
}
};
xhr.send();
Another way is to load the image and then compare some pixels colors. The "no streetview" image from google is always the same. Here is how you would compare 2 pixels:
var url = STREETVIEWURL
var img = new Image();
// Add some info to prevent cross origin tainting
img.src = url + '?' + new Date().getTime();
img.setAttribute('crossOrigin', '');
img.crossOrigin = "Anonymous";
img.onload = function() {
var context = document.createElement('CANVAS').getContext('2d');
context.drawImage(img, 0, 0);
//load 2 pixels. I chose the first one and the 5th row
var data1 = context.getImageData(0, 0, 1, 1).data;
var data2 = context.getImageData(0, 5, 1, 1).data;
console.log(data1);
// google unknown image is this pixel color [228,227,223,255]
if(data1[0]==228 && data1[1]==227 && data1[2]==223 && data1[3]==255 &&
data2[0]==228 && data2[1]==227 && data2[2]==223 && data2[3]==255){
console.log("NO StreetView Available");
}else{
console.log("StreetView is Available");
}
};
Some potential issues:
I've seen some errors with CrossOrigin tainting. Also, if google changes the image returned this code will break.

Resources