Orientacja Exif po stronie klienta JS: obracanie i lustrzane obrazy JPEG
Zdjęcia z aparatu cyfrowego są często zapisywane w FORMACIE JPEG z tagiem Exif "orientation". Aby wyświetlać poprawnie, obrazy muszą być obracane/lustrzane w zależności od ustawionej orientacji, ale przeglądarki ignorują te informacje renderujące obraz. Nawet w dużych komercyjnych aplikacjach internetowych obsługa orientacji EXIF może być spotty 1. To samo źródło zawiera również ładne podsumowanie 8 różnych orientacji JPEG może mieć:
Przykładowe zdjęcia są dostępne na 4.
Pytanie brzmi, jak obrócić / odbić obraz po stronie klienta, aby wyświetlał się poprawnie i w razie potrzeby mógł być dalej przetwarzany?
Dostępne są biblioteki JS do przetwarzania danych EXIF, w tym atrybut orientation 2. Flickr zauważył możliwy problem z wydajnością podczas parsowania dużych obrazów, wymagający użycia webworkerów 3.
Narzędzia konsoli mogą poprawnie przekierować obrazy 5. Skrypt PHP rozwiązujący problem jest dostępny na 6
9 answers
Projekt github JavaScript-Load-Image zapewnia kompletne rozwiązanie problemu orientacji EXIF, prawidłowo obracając / dublując obrazy dla wszystkich 8 orientacji exif. Zobacz demo online javascript Exif orientation
Obraz jest rysowany na płótnie HTML5. Jego poprawne renderowanie jest zaimplementowane w js / load-image-orientation.js poprzez operacje canvas.
Mam nadzieję, że to zaoszczędzi komuś trochę czasu i nauczy wyszukiwarek o tym Gem open source:)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-15 22:41:21
Transformata kontekstu Mederra działa doskonale. Jeśli chcesz wyodrębnić orientację użyj tylko tej funkcji - nie potrzebujesz żadnych bibliotek odczytu EXIF. Poniżej znajduje się funkcja do ponownego ustawienia orientacji w obrazie base64. / Align = "left" / Przygotowałem również DEMO fiddle with orientation extraction .
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;
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-25 16:46:54
If
width = img.width;
height = img.height;
var ctx = canvas.getContext('2d');
Następnie możesz użyć tych przekształceń, aby zmienić orientację obrazu na 1
Od orientacja:
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);
Przed narysowaniem obrazu na ctx
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-25 16:47:21
Ok oprócz odpowiedzi @user3096626 myślę, że bardziej pomocne będzie, jeśli ktoś poda przykład kodu, poniższy przykład pokaże, jak naprawić orientację obrazu pochodzi z adresu url (zdalne obrazy):
RozwiÄ…zanie 1: Korzystanie z javascript (zalecane)
-
Ponieważ load-image biblioteka nie wyodrębnia znaczników exif tylko z obrazów url (plik/blob), użyjemy obu exif-js oraz load-image biblioteki javascript, więc najpierw dodaj te biblioteki do swojej strony w następujący sposób:
<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>
Uwaga Wersja 2.2 Exif-js wydaje się mieć problemy, więc użyliśmy 2.1
-
Wtedy w zasadzie to co zrobimy to
A-Załaduj obraz za pomocą
window.loadImage()
B-odczyt znaczników exif za pomocą
window.EXIF.getData()
C-Konwertuj obraz na płótno i napraw orientację obrazu za pomocą
window.loadImage.scale()
D-umieść płótno w dokumencie
ProszÄ™ bardzo :)
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);
});
}
});
Oczywiście możesz również pobrać obraz jako base64 z obiektu canvas i umieścić go w atrybucie img src, więc używając jQuery możesz to zrobić;)
$("#my-image").attr("src",canvas.toDataURL());
Oto Pełny kod na github: https://github.com/digital-flowers/loadimage-exif-example
RozwiÄ…zanie 2: Korzystanie z html (przeglÄ…darka hack)
Jest bardzo szybki i łatwy hack, większość przeglądarek wyświetla obraz w odpowiedniej orientacji, jeśli obraz jest otwarty wewnątrz nowej karty bezpośrednio bez żadnego html( LOL Nie wiem dlaczego), więc w zasadzie możesz wyświetlić swój obraz za pomocą iframe, umieszczając atrybut iframe src jako adres URL obrazu bezpośrednio:
<iframe src="/my-image.jpg"></iframe>
RozwiÄ…zanie 3: Korzystanie z css (tylko firefox i safari na ios)
Jest atrybut css3, aby naprawić orientację obrazu, ale problem działa tylko na Firefoksie i safari/ios nadal warto wspomnieć, ponieważ wkrótce będzie dostępny dla wszystkich przeglądarek (informacje o wsparciu przeglądarki z caniuse)
img {
image-orientation: from-image;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-08 16:33:13
Odpowiedź Wunderbarta była dla mnie najlepsza. Zauważ, że możesz go znacznie przyspieszyć, jeśli Twoje obrazy są często we właściwym kierunku, po prostu najpierw testując orientację i omijając resztę kodu, jeśli nie jest wymagane obracanie.
Składanie wszystkich informacji z wunderbart razem, coś w tym stylu;
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 10:18:18
Dla tych, którzy mają Plik z kontrolki wejściowej, nie wiedzą, jaka jest jego orientacja, są trochę leniwi i nie chcą dołączać dużej biblioteki poniżej znajduje się kod dostarczony przez @wunderbart połączony z odpowiedzią, do której linkuje ( https://stackoverflow.com/a/32490603 ), który znajduje orientację.
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);
}
Które można łatwo nazwać tak
getDataUrl(input.files[0], function (imgBase64) {
vm.user.BioPhoto = imgBase64;
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-18 16:28:12
Używam rozwiązania mieszanego (php+css).
Pojemniki sÄ… potrzebne do:
-
div.imgCont2
pojemnik potrzebny do obracania; -
div.imgCont1
kontener potrzebny do powiększenia -width:150%
; -
div.imgCont
kontener potrzebny do scrollbarów, gdy obraz jest powiększony.
.
<?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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-02 11:28:47
Oprócz odpowiedzi @fareed namrouti,
To powinno być używane, jeśli obraz ma być przeglądany z elementu wejściowego pliku
<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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-08 19:04:51
Napisałem mały skrypt php, który obraca obraz. Pamiętaj, aby zapisać obraz na rzecz po prostu przeliczyć go na każde żądanie.
<?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);
?>
Cheers
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-18 16:12:07