Ориентация Exif на стороне клиента JS: поворот и зеркальное отображение изображений JPEG
фотографии с цифровой камеры часто сохраняются в формате JPEG с тегом EXIF "ориентация". Для правильного отображения изображения необходимо поворачивать / зеркально отображать в зависимости от того, какая ориентация установлена, но браузеры игнорируют эту информацию, отображая изображение. Даже в больших коммерческих веб-приложениях поддержка ориентации EXIF может быть пятнистой 1. Тот же источник также предоставляет хорошее резюме 8 различных ориентациях JPEG может есть:

образцы изображений доступны по адресу 4.
вопрос в том, как повернуть / отразить изображение на стороне клиента, чтобы оно отображалось правильно и могло быть дополнительно обработано при необходимости?
существуют библиотеки JS, доступные для анализа данных EXIF, включая атрибут orientation 2. Flickr отметил возможную проблему производительности при разборе больших изображений, требующую использования веб-мастеров 3.
консольные инструменты могут правильно переориентировать изображения 5. PHP-скрипт, решающий эту проблему, доступен по адресу 6
9 ответов:
проект github JavaScript-Load-Image обеспечивает полное решение проблемы ориентации EXIF, правильно вращая / отражая изображения для всех 8 ориентаций exif. Смотрите онлайн демо javascript exif ориентация
изображение рисуется на холсте HTML5. Его корректная визуализация реализована в js / load-Image-orientation.js через операции холсте.
надеюсь, что это сэкономит кому-то еще некоторое время, и учит поисковые системы об этом драгоценном камне с открытым исходным кодом:)
преобразование контекста Медерра отлично работает. Если вам нужно извлечь ориентацию, используйте только эта функция - вам не нужны никакие библиотеки 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; };
если
width = img.width; height = img.height; var ctx = canvas.getContext('2d');затем вы можете использовать эти преобразования, чтобы превратить изображение в ориентацию 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
ok в дополнение к ответу @user3096626 я думаю, что будет более полезно, если кто-то предоставил пример кода, следующий пример покажет вам, как исправить ориентацию изображения из url (удаленные изображения):
Решение 1: использование javascript (рекомендуется)
, потому что load-image библиотека не извлекает теги exif только из изображений url (file / 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>Примечание версия 2.2 exif-js, кажется, имеет проблемы, поэтому мы использовали 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); }); } });конечно, вы также можете получить изображение как base64 из объекта canvas и поместить его в атрибут img src, поэтому с помощью jQuery вы можете сделать ;)
$("#my-image").attr("src",canvas.toDataURL());вот полный код на: github: https://github.com/digital-flowers/loadimage-exif-example
решение 2: использование html (браузер hack)
существует очень быстрый и простой хак, большинство браузеров отображают изображение в правильной ориентации, если изображение открыто внутри новой вкладки напрямую без html (LOL я не знаю, почему), поэтому в основном вы можете отображать свое изображение с помощью iframe, поместив атрибут iframe src в качестве url-адреса изображения непосредственно:
<iframe src="/my-image.jpg"></iframe>
решение 3: использование css (только firefox и safari на ios)
есть атрибут css3 для исправления ориентации изображения, но проблема в том, что он работает только на firefox и safari / ios, все еще стоит упомянуть, потому что скоро он будет доступен для всех браузеров (информация о поддержке браузера от caniuse)
img { image-orientation: from-image; }
ответ 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; }
для тех, у кого есть файл из элемента управления вводом, не знаю, какова его ориентация, немного ленивы и не хотят включать большую библиотеку ниже приведен код, предоставленный @WunderBart, объединенный с ответом, на который он ссылается (https://stackoverflow.com/a/32490603), который находит ориентацию.
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); }который легко можно назвать таковым
getDataUrl(input.files[0], function (imgBase64) { vm.user.BioPhoto = imgBase64; });
я использую смешанное решение (php+css).
контейнеры необходимы для:
div.imgCont2контейнер необходимо повернуть;div.imgCont1контейнер, необходимый для масштабирования -width:150%;div.imgContконтейнер, необходимый для полос прокрутки, когда изображение масштабируется..
<?php $image_url = 'your image url.jpg'; $exif = @exif_read_data($image_url,0,true); $orientation = @$exif['IFD0']['Orientation']; ?> <style> .imgCont{ width:100%; overflow:auto; } .imgCont2[data-orientation="8"]{ transform:rotate(270deg); margin:15% 0; } .imgCont2[data-orientation="6"]{ transform:rotate(90deg); margin:15% 0; } .imgCont2[data-orientation="3"]{ transform:rotate(180deg); } img{ width:100%; } </style> <div class="imgCont"> <div class="imgCont1"> <div class="imgCont2" data-orientation="<?php echo($orientation) ?>"> <img src="<?php echo($image_url) ?>"> </div> </div> </div>
в дополнение к ответу @fareed namrouti,
Это должно использоваться, если изображение должно быть просмотрено из элемента ввода файла
<input type="file" name="file" id="file-input"><br/> image after transform: <br/> <div id="container"></div> <script> document.getElementById('file-input').onchange = function (e) { var image = e.target.files[0]; window.loadImage(image, function (img) { if (img.type === "error") { console.log("couldn't load image:", img); } else { window.EXIF.getData(image, function () { console.log("load image done!"); var orientation = window.EXIF.getTag(this, "Orientation"); var canvas = window.loadImage.scale(img, {orientation: orientation || 0, canvas: true, maxWidth: 200}); document.getElementById("container").appendChild(canvas); // or using jquery $("#container").append(canvas); }); } }); }; </script>
Я написал небольшой PHP скрипт, который поворачивает изображение. Не забудьте сохранить изображение в пользу просто пересчитать его каждый запрос.
<?php header("Content-type: image/jpeg"); $img = 'IMG URL'; $exif = @exif_read_data($img,0,true); $orientation = @$exif['IFD0']['Orientation']; if($orientation == 7 || $orientation == 8) { $degrees = 90; } elseif($orientation == 5 || $orientation == 6) { $degrees = 270; } elseif($orientation == 3 || $orientation == 4) { $degrees = 180; } else { $degrees = 0; } $rotate = imagerotate(imagecreatefromjpeg($img), $degrees, 0); imagejpeg($rotate); imagedestroy($rotate); ?>Ура
Comments