fle*_*ive 131 javascript exif rotation html5-canvas
数码相机照片通常以JPEG格式保存,并带有EXIF"方向"标签.要正确显示,需要根据设置的方向旋转/镜像图像,但浏览器会忽略渲染图像的信息.即使在大型商业网络应用程序中,对EXIF方向的支持也可能不稳定1.相同的源代码还提供 了JPEG可以具有的8种不同方向的精彩摘要:
样本图像可在4处获得.
问题是如何在客户端旋转/镜像图像,以便正确显示并在必要时进一步处理?
有JS库可用于解析EXIF数据,包括orientation属性2.Flickr在解析大图像时注意到了可能的性能问题,需要使用Web工作者3.
fle*_*ive 132
github项目JavaScript-Load-Image为EXIF方向问题提供了一个完整的解决方案,正确旋转/镜像所有8个exif方向的图像.查看javascript exif方向的在线演示
图像被绘制到HTML5画布上.它的正确渲染是通过canvas操作在js/load-image-orientation.js中实现的.
希望这能节省一些时间,并教导搜索引擎关于这个开源宝石:)
Wun*_*art 87
Mederr的上下文转换非常有效.如果您需要提取方向,请仅使用此功能 - 您不需要任何EXIF读取库.以下是在base64图像中重新设置方向的功能. 这是一个小提琴.我还准备了一个方向提取演示的小提琴.
function resetOrientation(srcBase64, srcOrientation, callback) {
var img = new Image();
img.onload = function() {
var width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
// set proper canvas dimensions before transform & export
if (4 < srcOrientation && srcOrientation < 9) {
canvas.width = height;
canvas.height = width;
} else {
canvas.width = width;
canvas.height = height;
}
// transform context before drawing image
switch (srcOrientation) {
case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
case 7: ctx.transform(0, -1, -1, 0, height, width); break;
case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
ctx.drawImage(img, 0, 0);
// export base64
callback(canvas.toDataURL());
};
img.src = srcBase64;
};
Run Code Online (Sandbox Code Playgroud)
Med*_*err 38
如果
width = img.width;
height = img.height;
var ctx = canvas.getContext('2d');
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用这些转换将图像转换为方向1
从方向:
ctx.transform(1, 0, 0, 1, 0, 0);
ctx.transform(-1, 0, 0, 1, width, 0);
ctx.transform(-1, 0, 0, -1, width, height);
ctx.transform(1, 0, 0, -1, 0, height);
ctx.transform(0, 1, 1, 0, 0, 0);
ctx.transform(0, 1, -1, 0, height, 0);
ctx.transform(0, -1, -1, 0, height, width);
ctx.transform(0, -1, 1, 0, 0, width);
在ctx上绘制图像之前
Far*_*uti 22
好的,除了@ user3096626,我认为如果有人提供代码示例会更有帮助,下面的例子将向您展示如何修复图像方向来自url(远程图像):
解决方案1:使用javascript(推荐)
因为加载图像库不会仅从url图像中提取exif标记(文件/ blob),所以我们将使用exif-js和load-image javascript库,因此首先将这些库添加到页面中,如下所示:
<script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.1.0/exif.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-scale.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-orientation.min.js"></script>
Run Code Online (Sandbox Code Playgroud)
注意 exif-js的2.2版似乎有问题所以我们使用了2.1
那基本上我们要做的是
a - 使用加载图像 window.loadImage()
b - 使用读取exif标签 window.EXIF.getData()
c - 将图像转换为画布并使用固定图像方向 window.loadImage.scale()
d - 将画布放入文档中
干得好 :)
window.loadImage("/your-image.jpg", function (img) {
if (img.type === "error") {
console.log("couldn't load image:", img);
} else {
window.EXIF.getData(img, function () {
var orientation = EXIF.getTag(this, "Orientation");
var canvas = window.loadImage.scale(img, {orientation: orientation || 0, canvas: true});
document.getElementById("container").appendChild(canvas);
// or using jquery $("#container").append(canvas);
});
}
});
Run Code Online (Sandbox Code Playgroud)
当然你也可以从canvas对象获取base64的图像并将其放在img src属性中,所以使用jQuery你可以做;)
$("#my-image").attr("src",canvas.toDataURL());
Run Code Online (Sandbox Code Playgroud)
这里是完整的代码:github:https://github.com/digital-flowers/loadimage-exif-example
解决方案2:使用html(浏览器黑客)
有一个非常快速和简单的黑客,大多数浏览器以正确的方向显示图像如果图像是直接在新的选项卡内打开没有任何HTML(LOL我不知道为什么),所以基本上你可以使用iframe显示你的图像将iframe src属性直接作为图像url:
<iframe src="/my-image.jpg"></iframe>
Run Code Online (Sandbox Code Playgroud)
解决方案3:使用css(ios上只有firefox和safari)
有css3属性来修复图像方向但问题是它只适用于firefox和safari/ios它仍然值得一提,因为很快它将适用于所有浏览器(来自caniuse的浏览器支持信息)
img {
image-orientation: from-image;
}
Run Code Online (Sandbox Code Playgroud)
对于那些有输入控件文件的人,不知道它的方向是什么,有点懒,不想在下面包含一个大型库,@ WunderBart提供的代码与他链接的答案融合在一起(/sf/answers/2274342241/)找到方向.
function getDataUrl(file, callback2) {
var callback = function (srcOrientation) {
var reader2 = new FileReader();
reader2.onload = function (e) {
var srcBase64 = e.target.result;
var img = new Image();
img.onload = function () {
var width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
// set proper canvas dimensions before transform & export
if (4 < srcOrientation && srcOrientation < 9) {
canvas.width = height;
canvas.height = width;
} else {
canvas.width = width;
canvas.height = height;
}
// transform context before drawing image
switch (srcOrientation) {
case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
case 7: ctx.transform(0, -1, -1, 0, height, width); break;
case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
ctx.drawImage(img, 0, 0);
// export base64
callback2(canvas.toDataURL());
};
img.src = srcBase64;
}
reader2.readAsDataURL(file);
}
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, offset = 2;
while (offset < length) {
var marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
var little = view.getUint16(offset += 6, 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(-1);
};
reader.readAsArrayBuffer(file);
}
Run Code Online (Sandbox Code Playgroud)
这很容易被称为
getDataUrl(input.files[0], function (imgBase64) {
vm.user.BioPhoto = imgBase64;
});
Run Code Online (Sandbox Code Playgroud)
我还没有看到有人提到browser-image-compression
图书馆。它有一个完美的辅助函数。
用法: const orientation = await imageCompression.getExifOrientation(file)
在许多其他方面也是如此有用的工具。
WunderBart的答案对我来说是最好的.请注意,如果您的图像通常是正确的方式,您可以加快速度,只需首先测试方向,如果不需要旋转则绕过其余代码.
把所有来自wunderbart的信息放在一起,就像这样;
var handleTakePhoto = function () {
let fileInput: HTMLInputElement = <HTMLInputElement>document.getElementById('photoInput');
fileInput.addEventListener('change', (e: any) => handleInputUpdated(fileInput, e.target.files));
fileInput.click();
}
var handleInputUpdated = function (fileInput: HTMLInputElement, fileList) {
let file = null;
if (fileList.length > 0 && fileList[0].type.match(/^image\//)) {
isLoading(true);
file = fileList[0];
getOrientation(file, function (orientation) {
if (orientation == 1) {
imageBinary(URL.createObjectURL(file));
isLoading(false);
}
else
{
resetOrientation(URL.createObjectURL(file), orientation, function (resetBase64Image) {
imageBinary(resetBase64Image);
isLoading(false);
});
}
});
}
fileInput.removeEventListener('change');
}
// from http://stackoverflow.com/a/32490603
export function getOrientation(file, callback) {
var reader = new FileReader();
reader.onload = function (event: any) {
var view = new DataView(event.target.result);
if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
var length = view.byteLength,
offset = 2;
while (offset < length) {
var marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
if (view.getUint32(offset += 2, false) != 0x45786966) {
return callback(-1);
}
var little = view.getUint16(offset += 6, 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(-1);
};
reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
};
export function resetOrientation(srcBase64, srcOrientation, callback) {
var img = new Image();
img.onload = function () {
var width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
// set proper canvas dimensions before transform & export
if (4 < srcOrientation && srcOrientation < 9) {
canvas.width = height;
canvas.height = width;
} else {
canvas.width = width;
canvas.height = height;
}
// transform context before drawing image
switch (srcOrientation) {
case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
case 7: ctx.transform(0, -1, -1, 0, height, width); break;
case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
ctx.drawImage(img, 0, 0);
// export base64
callback(canvas.toDataURL());
};
img.src = srcBase64;
}
Run Code Online (Sandbox Code Playgroud)