JPEG EXIF画像データでカメラが設定した元の回転に基づいて写真を回転させたい。秘Theは、JavaScriptと<canvas>
を使用して、これらすべてをブラウザーで行う必要があるということです。
JavaScriptは、JPEG、ローカルファイルAPIオブジェクト、ローカル<img>
またはリモート<img>
、EXIFデータにアクセスして、回転情報を読み取る方法を教えてください。
サーバー側の答えは大丈夫ではありません。 クライアント側ソリューションを探しています。
オリエンテーションタグのみが必要で、別の巨大なJavaScriptライブラリを含めたくない場合は、オリエンテーションタグのみを可能な限り高速に抽出する小さなコードを記述します(IE10 +で使用可能なDataViewとreadAsArrayBuffer
を使用します、ただし、古いブラウザ用に独自のデータリーダーを作成できます)。
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, offset = 2;
while (offset < length)
{
if (view.getUint16(offset+2, false) <= 8) return callback(-1);
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);
}
// usage:
var input = document.getElementById('input');
input.onchange = function(e) {
getOrientation(input.files[0], function(orientation) {
alert('orientation: ' + orientation);
});
}
<input id='input' type='file' />
値:
-2: not jpeg
-1: not defined
TypeScriptを使用している場合は、次のコードを使用できます。
export const getOrientation = (file: File, callback: Function) => {
var reader = new FileReader();
reader.onload = (event: ProgressEvent) => {
if (! event.target) {
return;
}
const file = event.target as FileReader;
const view = new DataView(file.result as ArrayBuffer);
if (view.getUint16(0, false) != 0xFFD8) {
return callback(-2);
}
const length = view.byteLength
let offset = 2;
while (offset < length)
{
if (view.getUint16(offset+2, false) <= 8) return callback(-1);
let marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
if (view.getUint32(offset += 2, false) != 0x45786966) {
return callback(-1);
}
let little = view.getUint16(offset += 6, false) == 0x4949;
offset += view.getUint32(offset + 4, little);
let tags = view.getUint16(offset, little);
offset += 2;
for (let 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);
}
exif-js ライブラリをHTML5 File APIと組み合わせて使用できます: http://jsfiddle.net/xQnMd/1/ 。
$("input").change(function() {
var file = this.files[0]; // file
fr = new FileReader; // to read file contents
fr.onloadend = function() {
// get EXIF data
var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));
// alert a value
alert(exif.Make);
};
fr.readAsBinaryString(file); // read the file
});
Firefox 26はimage-orientation: from-image
をサポートします。画像はEXIFデータに応じて縦向きまたは横向きで表示されます。 ( sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation を参照してください。)
Chromeでこれを実装するバグ もあります。
このプロパティはFirefoxでのみサポートされており、 非推奨の可能性が高い であることに注意してください。
https://github.com/blueimp/JavaScript-Load-Image は、exif方向フラグを抽出できるだけでなく、クライアント側でJPEG画像を正しくミラーリング/回転できる最新のJavaScriptライブラリです。 。
このライブラリで同じ問題を解決しました: JSクライアント側Exif方向:JPEG画像の回転とミラーリング
クロスブラウザが必要な場合は、サーバーで実行するのが最善の策です。ファイルURLを受け取り、EXIFデータを返すAPIを使用できます。 PHPにはそのためのモジュールがあります 。
これは Ajax を使用して実行できるため、ユーザーにとってシームレスです。ブラウザー間の互換性を気にせず、 HTML5 ファイル機能に依存できる場合は、ライブラリ JsJPEGmeta を調べて、ネイティブJavaScriptでそのデータを取得できるようにします。 。
Exifの向きをCSS変換に変換する、私が書いたモジュール(ブラウザーで使用できます)を確認してください。 https://github.com/Sobesednik/exif2css
すべての方向のJPEGフィクスチャを生成するこのノードプログラムもあります。 https://github.com/Sobesednik/generate-exif-fixtures
拡張コードをアップロードしますHTMLのAndroidカメラで通常のように右回転のimgタグ、特に幅が高さより広いimgタグでカメラに表示するためにこのコードを知っていますisいですが、他のパッケージをインストールする必要はありません。 (exif回転値を取得するために上記のコードを使用しました、ありがとうございます。)
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, 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);
}
var isChanged = false;
function rotate(elem, orientation) {
if (isIPhone()) return;
var degree = 0;
switch (orientation) {
case 1:
degree = 0;
break;
case 2:
degree = 0;
break;
case 3:
degree = 180;
break;
case 4:
degree = 180;
break;
case 5:
degree = 90;
break;
case 6:
degree = 90;
break;
case 7:
degree = 270;
break;
case 8:
degree = 270;
break;
}
$(elem).css('transform', 'rotate('+ degree +'deg)')
if(degree == 90 || degree == 270) {
if (!isChanged) {
changeWidthAndHeight(elem)
isChanged = true
}
} else if ($(elem).css('height') > $(elem).css('width')) {
if (!isChanged) {
changeWidthAndHeightWithOutMargin(elem)
isChanged = true
} else if(degree == 180 || degree == 0) {
changeWidthAndHeightWithOutMargin(elem)
if (!isChanged)
isChanged = true
else
isChanged = false
}
}
}
function changeWidthAndHeight(elem){
var e = $(elem)
var width = e.css('width')
var height = e.css('height')
e.css('width', height)
e.css('height', width)
e.css('margin-top', ((getPxInt(height) - getPxInt(width))/2).toString() + 'px')
e.css('margin-left', ((getPxInt(width) - getPxInt(height))/2).toString() + 'px')
}
function changeWidthAndHeightWithOutMargin(elem){
var e = $(elem)
var width = e.css('width')
var height = e.css('height')
e.css('width', height)
e.css('height', width)
e.css('margin-top', '0')
e.css('margin-left', '0')
}
function getPxInt(pxValue) {
return parseInt(pxValue.trim("px"))
}
function isIPhone(){
return (
(navigator.platform.indexOf("iPhone") != -1) ||
(navigator.platform.indexOf("iPod") != -1)
);
}
そして次に
$("#banner-img").change(function () {
var reader = new FileReader();
getOrientation(this.files[0], function(orientation) {
rotate($('#banner-img-preview'), orientation, 1)
});
reader.onload = function (e) {
$('#banner-img-preview').attr('src', e.target.result)
$('#banner-img-preview').css('display', 'inherit')
};
// read the image file as a data URL.
reader.readAsDataURL(this.files[0]);
});
以前のALiの回答に機能を追加/追加し、この問題のニーズに合ったutilメソッドをTypeScriptで作成しました。このバージョンは、プロジェクトに必要な回転角度を度単位で返します。
ImageUtils.ts
/**
* Based on StackOverflow answer: https://stackoverflow.com/a/32490603
*
* @param imageFile The image file to inspect
* @param onRotationFound callback when the rotation is discovered. Will return 0 if if it fails, otherwise 0, 90, 180, or 270
*/
export function getOrientation(imageFile: File, onRotationFound: (rotationInDegrees: number) => void) {
const reader = new FileReader();
reader.onload = (event: ProgressEvent) => {
if (!event.target) {
return;
}
const innerFile = event.target as FileReader;
const view = new DataView(innerFile.result as ArrayBuffer);
if (view.getUint16(0, false) !== 0xffd8) {
return onRotationFound(convertRotationToDegrees(-2));
}
const length = view.byteLength;
let offset = 2;
while (offset < length) {
if (view.getUint16(offset + 2, false) <= 8) {
return onRotationFound(convertRotationToDegrees(-1));
}
const marker = view.getUint16(offset, false);
offset += 2;
if (marker === 0xffe1) {
if (view.getUint32((offset += 2), false) !== 0x45786966) {
return onRotationFound(convertRotationToDegrees(-1));
}
const little = view.getUint16((offset += 6), false) === 0x4949;
offset += view.getUint32(offset + 4, little);
const tags = view.getUint16(offset, little);
offset += 2;
for (let i = 0; i < tags; i++) {
if (view.getUint16(offset + i * 12, little) === 0x0112) {
return onRotationFound(convertRotationToDegrees(view.getUint16(offset + i * 12 + 8, little)));
}
}
// tslint:disable-next-line:no-bitwise
} else if ((marker & 0xff00) !== 0xff00) {
break;
} else {
offset += view.getUint16(offset, false);
}
}
return onRotationFound(convertRotationToDegrees(-1));
};
reader.readAsArrayBuffer(imageFile);
}
/**
* Based off snippet here: https://github.com/mosch/react-avatar-editor/issues/123#issuecomment-354896008
* @param rotation converts the int into a degrees rotation.
*/
function convertRotationToDegrees(rotation: number): number {
let rotationInDegrees = 0;
switch (rotation) {
case 8:
rotationInDegrees = 270;
break;
case 6:
rotationInDegrees = 90;
break;
case 3:
rotationInDegrees = 180;
break;
default:
rotationInDegrees = 0;
}
return rotationInDegrees;
}
使用法:
import { getOrientation } from './ImageUtils';
...
onDrop = (pics: any) => {
getOrientation(pics[0], rotationInDegrees => {
this.setState({ image: pics[0], rotate: rotationInDegrees });
});
};