-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Handling EXIF Image Orientation #120
Comments
Thank you very much! I will handle it if it is possible. |
I have fixed a bug when replace the image. Please try the new version 0.7.5 to check if it still occurs this problem. |
My thanks as well for the fabulous plugin - I really appreciate the hard work you've clearly put into it. I've updated to version 0.7.5 and unfortunately I'm still having the same issue as reported by @bameyrick. On an iOS device, it often processes a section of the image different from what's been selected in the GUI probably due to how iOS adjusts the image display based on EXIF Orientation data. In my tests, an EXIF Orientation of 6 produces the issue because iOS rotates the image 90 degrees clockwise so that it displays 'correctly'. If that's the case, then I would also guess that EXIF Orientations of 3 and 8 would cause problems too since iOS would rotate those images 180 degrees and 90 degrees counter-clockwise respectively. Forgive my corny test picture, but it shows the Chrome browser rendering the same photograph (with an EXIF Orientation of 6) on my desktop Mac and my iPhone 5: If you have any suggestions on what I might be able to do to address this, they'd be greatly appreciated. Thanks again! |
Having the same issue. Anybody found a workaround for this issue? |
Maybe it is more easy to handle the EXIF Orientation on the server-side. |
First, thanks for an outstanding plug-in!!! I poked around to see if there was a client-side library I could use to do the EXIF correctio. There are a couple of JS EXIF readers (like https://github.com/jseidelin/exif-js), and I think I can use that info, load the image into a canvas, rotate the image in the canvas, and then get the data. I'll play around with this more and post any luck I have. In the meantime, for .NET land, I put together a c# static method (which I wired up to a WebAPI, but you can use it with WCF or whatever). You'll need to add a project reference to System.Drawing (and add namespaces to your class for System.Drawing, System.Drawing.Imaging and System.Text).
Edit: Make sure to call this before loading image into cropper, not after |
Thank you @jasonterando . |
Did you guys look further into using exif-js? |
If you're getting an image from the user, you can use https://github.com/blueimp/JavaScript-Load-Image to get it into the correct orientation before using cropper: $('#yourFileInput').on('change', function(e) {
e.preventDefault();
e = e.originalEvent;
var target = e.dataTransfer || e.target,
file = target && target.files && target.files[0],
options = {
canvas: true
};
if (!file) {
return;
}
// Use the "JavaScript Load Image" functionality to parse the file data
loadImage.parseMetaData(file, function(data) {
// Get the correct orientation setting from the EXIF Data
if (data.exif) {
options.orientation = data.exif.get('Orientation');
}
// Load the image from disk and inject it into the DOM with the correct orientation
loadImage(
file,
function(canvas) {
var imgDataURL = canvas.toDataURL();
var $img = $('<img>').attr('src', imgDataURL);
$('body').append($img);
// Initiate cropper once the orientation-adjusted image is in the DOM
$img.cropper();
},
options
);
});
}); |
@bbrooks nice work,you saved my day,awesome plugin. |
I encountered the same problem with iOS devices while I was adapting the Crop Avatar example to my own purposes. The preview would look fine in the web browser, but once uploaded and cropped (and manipulated on the back-end), it had the wrong orientation. I solved this on the PHP side. In that example's cropper.php file, in the setFile function, starting at line 54, there is this block:
Into this block, I incorporated the solution described on StackExchange by user462990. So that whole block now reads:
There's probably a better way to handle this, but this works for me for now. |
@dekortage If the user rotate the image with cropper on browser side, and you rotate it again on server side, then what will happen? |
I am using exif-js to determine orientation and rotate initially in browser. Server uses data from cropper. Here is my code. Note that there any 1-8 orientations in exif. EXIF.getData img.get(0), () ->
orientation = EXIF.getTag(this, "Orientation")
# console.log "orientation", orientation
img.cropper
strict: false
aspectRatio: 1.47
crop: (e) ->
$("#photo_offset").val(JSON.stringify(e))
# rotate according to orientation
switch orientation
when 2
img.cropper('scale', -1, 1) # Flip horizontal
when 3
img.cropper('scale', -1)
when 4
img.cropper('scale', 1, -1)
when 5
img.cropper('scale', -1, 1)
img.cropper('rotate', -90)
when 6
img.cropper('rotate', 90)
when 7
img.cropper('scale', 1, -1)
img.cropper('rotate', -90)
when 8
img.cropper('rotate', -90)
$(".rotate-left").off("click")
$(".rotate-left").on "click", (e) ->
e.preventDefault()
e.stopPropagation()
img.cropper('rotate', 90) On server I'm using ruby-on-rails and carrierwave to handle uploads. Code uses Rmagick. Here is code. It extracts x,y,w,h,rotate,scale values that cropper sends to server. manipulate! do |img|
Rails.logger.debug "[PHOTO] offset: #{model.photo_offset.pretty_inspect}".yellow
x, y, w, h, rotate, scaleX, scaleY = model.photo_offset.values_at("x", "y",
"width", "height", "rotate", "scaleX", "scaleY").map { |v| v.to_f.round(2) }
crop = "#{w}x#{h}!+#{x}+#{y}"
Rails.logger.debug "[PHOTO] crop: #{crop.inspect}".green
# img.auto_orient!
img.flip! if scaleY == -1
img.flop! if scaleX == -1
img.rotate!(rotate) if rotate.present? && !rotate.zero?
img.crop!(x,y,w,h)
img.strip!
img = yield(img) if block_given?
img
end Blog post about exif rotations: http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ I don't have any problems with this example images, however MacOs guys complains so the code is not 100% complete. HTH |
@fengyuanchen -- it seems to work fine in my limited testing. The client side previews fine, and the transformations are sent up to the server as if the image is oriented correctly. It's just that the iOS device uploads the image at a 90° angle before applying the cropping/transformations. The server needs to reorient the file back to what the user saw client-side, before applying the server-side transformations. However, I would definitely welcome more extensive inquiry/experimentation into this. I only have a couple of iOS devices at my disposal. |
If the root problem is that sometimes the image rendered to a canvas is correctly orientated and sometimes it isn't, then given an image with width X and height Y then can't we determine which is longest and store that as H and the shortest as W, then render the image to a canvas of size HxH, then check the pixels at (W,H) and (H,W) to see which way round the image has been rendered, then rotate and crop the area as required, giving us the correctly orientated image in all cases, from which point we can then do anything we want, with no server manipulation needed? |
@MarcusJT What about a square image? |
Ha! Good point... damn... |
Update - 2015-11-18 : Edit - 2015-11-19 : <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/cropper.css">
</head>
<body>
<input type="file" id="file" accept=".jpg, .jpeg, .png, .gif">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="js/plugin/cropper.min.js"></script>
<script>
$(document).ready(function () {
$('#file').change(function () {
var file = this.files[0];
getOrientation(file, function (ori) { // 1. get exif
reader(file, ori ? ori : 1);
});
});
function getOrientation(file, callback) {
var reader = new FileReader();
reader.onload = function(e) {
var view = new DataView(e.target.result);
if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
var length = view.byteLength;
var offset = 2;
while (offset < length) {
var marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
var little = view.getUint16(offset += 8, false) == 0x4949;
offset += view.getUint32(offset + 4, little);
var tags = view.getUint16(offset, little);
offset += 2;
for (var i = 0; i < tags; i++)
if (view.getUint16(offset + (i * 12), little) == 0x0112)
return callback(view.getUint16(offset + (i * 12) + 8, little));
}
else if ((marker & 0xFF00) != 0xFF00) break;
else offset += view.getUint16(offset, false);
}
return callback(0); // +- edit
};
reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
}
function reader(file, ori) {
var reader = new FileReader(); // 2. FileReader
reader.onload = function (e) {
var img = new Image();
img.onload = function () {
if (ori === 1 || ori === 3) {
var w = this.width;
var h = this.height;
} else {
var h = this.width;
var w = this.height;
}
//if (ori === 1) { // -- removed
// var img0 = img; // -- removed
//} else { // -- removed
var canvas = document.createElement('canvas'); // 3. canvas
var ctx = canvas.getContext('2d');
canvas.width = w; // ++ added
canvas.height = h; // ++ added
if (ori === 1) { // ++ added
ctx.drawImage(img, 0, 0, w, h); // ++ added
} else if (ori === 3) { // 4. transform, drawImage
ctx.transform(-1, 0, 0, -1, w, h);
ctx.drawImage(img, 0, 0, w, h);
} else if (ori === 6) {
ctx.transform(0, 1, -1, 0, w, 0);
ctx.drawImage(img, 0, 0, h, w);
} else {
ctx.transform(0, -1, 1, 0, 0, h);
ctx.drawImage(img, 0, 0, h, w);
}
var img0 = canvas.toDataURL(); // 5. get image
//} // -- removed
// +- add max-height to keep size within page
$('body').html('<img id="image" style="max-height: '+ $(window).height() +'px;">');
$('#image').prop('src', img0).one('load', function () {
$(this).cropper();
});
}
img.src = e.target.result;
}
reader.readAsDataURL(file);
}
});
</script>
</body>
</html> |
@rern really great thanks! I used your solution about an hour ago. What I discovered is that in iOS (Chrome and Safari) the orientation is correct, without any workaround but if you try on Chrome desktop with a photo taken with an iPhone (with exif) it will generate the issue. This is is a per-browser issue. I will add more details as soon as I can. |
You can try the Loader to translate Exif Orientation by canvas and get a pure image for Cropper. |
@50bbx - I've tried Chrome on Windows 10. iPhone photos display correctly. |
@rern Sorry, you are right, I was using half of your solution and half of edbond. I see the image correctly rotated now. The problem is I only see a very small piece. This is the result: EDIT: I also think there is an error whe orientation is 1. Infact Please, do not get me wrong, I don't want to say you were wrong, I'm just trying to make this code work. I thank you for your help, it has been really appreciated. :) |
@fengyuanchen I've tried, I've used precisely @bbrooks solution but it was so slow I had to revert my changes. I'm sorry I cannot provide that code anymore. But I simply copied and pasted that solution in my code (that is pretty straight forward and doesn't do anything complex nor strange) |
@fengyuanchen I think @mjvestal is right, may be you should remove the 2 lines he mentioned from the code (if statement after //modern browsers). by the way thank you @mjvestal. |
@Win10Developer I shouldn't be answering here since it's not the best place to ask, but here it is:
|
with "checkOrientation: false" i have the same problem like @every2ndcountsxc with "checkOrientation: true" and version 2.2.5 the image is still not in the right direction.. :/ +1 for fixing this... PS.: really nice plugin 👍 |
i built the latest version like you described, the image is now rotated like it should, but my wrapper has now at the top and at the button space (see image) EDIT: checkOrientation: false/true does the same now |
Hi, everyone, please provide your iOS and Safari version here once you need to comment here. |
iOS 9.2.1 on iPhone 5S |
I'm using iOS 9.2 on an iPhone 6S. All images that I add to the cropper via selecting the "Take Photo" option are rotated incorrectly. Although, if the image is added via an already existing image, using the "Photo Library" option, the image is the correct rotation. Here is are screenshots of me using the "Take Photo" option: |
Can confirm that the following fixed iPhone uploads rotating for me:
And commenting out |
Can also confirm that the fix from @samrayner works well on Safari. Thanks for sharing. |
@samrayner's solution only seems to work some of the time. I believe it does not work for images that were taken as landscape. Unfortunately, there are still many issues with the cropper and image orientation. |
After weeks of debugging, I have found that @bbrooks solution with the |
Hi, I am also facing this problem. In my case I develop an hybrid (Cordova-based) iOS app, so I use this plugin within a UIWebView (not Safari). Therefore the |
Hi @ghybs can you say how you did the test for the UIWebView? I have the same problem with cordova |
@ghybs Could you create a pull request? |
Hi, As for detecting the UIWebView, for now I just detect an iOS device: This is not bullet proof in the case of a Web App, as I guess people could still use another browser on an iOS device. I will try to make a PR when I have time. |
I was having this problem, I did what @samrayner suggested and worked perfectly. |
fixed this using @bbrooks thanks |
|
Firstly, thanks for the great plugin! However I've noticed a small issue; When loading an image in with EXIF orientation, everything works as expected until the "getDataURL" method is called. The generated image does not crop correctly and the rotation of the cropped image is incorrect.
Below I've provided screen shots of the chosen crop area and the image generated by "Get Data URL (JPG)". The image was taken on an iPad in portrait mode.
The white area in the generated image only seems to happen when a crop is taken near the bottom the image.
Thanks
The text was updated successfully, but these errors were encountered: