क्लाइंट की तरफ जावास्क्रिप्ट में JPEG EXIF ​​रोटेशन डेटा एक्सेस करना


125

मैं अपने मूल रोटेशन के आधार पर फ़ोटो को घुमाना चाहता हूं, जैसा कि JPEG EXIF ​​छवि डेटा में कैमरे द्वारा निर्धारित किया गया है। चाल यह है कि जावास्क्रिप्ट और जावास्क्रिप्ट का उपयोग करके यह सब ब्राउज़र में होना चाहिए <canvas>

जावास्क्रिप्ट, JPEG, स्थानीय फ़ाइल API ऑब्जेक्ट, स्थानीय <img>या दूरस्थ <img>, EXIF ​​डेटा को रोटेशन की जानकारी पढ़ने के लिए कैसे उपयोग कर सकता है ?

सर्वर-साइड उत्तर ठीक नहीं हैं; मैं एक क्लाइंट-साइड समाधान की तलाश में हूं ।

जवाबों:


261

यदि आप केवल अभिविन्यास टैग चाहते हैं और कुछ नहीं और किसी अन्य विशाल जावास्क्रिप्ट लाइब्रेरी को शामिल करना पसंद नहीं करते हैं तो मैंने एक छोटा कोड लिखा है जो अभिविन्यास टैग को जितनी जल्दी हो सके (यह डेटा व्यू का उपयोग करता है और readAsArrayBufferजो IE10 + में उपलब्ध हैं, लेकिन आप लिख सकते हैं पुराने ब्राउज़रों के लिए आपका अपना डेटा रीडर):

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

यहाँ छवि विवरण दर्ज करें

टाइपस्क्रिप्ट का उपयोग करने वालों के लिए, आप निम्न कोड का उपयोग कर सकते हैं:

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);
}

सही छवि प्राप्त करने के लिए 2,4,5,7 के लिए आपको घुमाने और फ्लिप करने की आवश्यकता है, है ना?
मुहम्मद उमर

मेरी छवि का अभिविन्यास है 3. मैं 1 के लिए अभिविन्यास कैसे सेट करूँ?
लुसी

3
@ मिक्स PNG या GIF में इमेज ओरिएंटेशन stackoverflow.com/questions/9542359/… स्टोर करने के लिए कोई मानक प्रारूप नहीं है।
अली

2
मेरे लिए काम करना, लेकिन मुझे अंतिम लाइन को सिर्फ रीडर में बदलने की जरूरत थी। AreadAsArrayBuffer (फाइल) स्लाइस के बिना जैसा कि मैं अपने बेस 64 इमेज के लिए बफर का उपयोग करने का इरादा रखता हूं, अन्यथा, आपको बस छवि का पहला टुकड़ा दिखाई देगा। BTW, यह आवश्यक नहीं है अगर आपको सिर्फ अभिविन्यास की जानकारी की आवश्यकता है। धन्यवाद
फिलिप मर्फी

2
@DaraJava मैंने स्लाइस भाग को हटा दिया क्योंकि कभी-कभी टैग सीमा के बाद आता है, लेकिन यह टैग धीमा नहीं होने पर ऑपरेशन को धीमा कर देगा। वैसे भी, अभिविन्यास टैग के विपरीत, फ़्लैश टैग IFD0 निर्देशिका में नहीं है और मेरा कोड केवल इस भाग को खोजता है। फ्लैश टैग प्राप्त करने के लिए आपको SubIFD डायरेक्टरी को खोजना होगा। आप यहाँ EXIF ​​पर एक अच्छा ट्यूटोरियल पा सकते हैं: Media.mit.edu/pia/Research/deepview/exif.html
Ali

22

आप HTML5 फ़ाइल API के साथ संयोजन में exif-js लाइब्रेरी का उपयोग कर सकते हैं : 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
});

धन्यवाद। सवाल में जेएस का दायित्व थोड़ा पुराना लग रहा है, लेकिन शायद काम करेगा।
मिको ओकटामा

एक फ़ाइल अपलोड विजेट का मेरा डेमो भी देखें जो मैंने अभी लिखा था। यह छवि फ़ाइल के मेटाडेटा में EXIF ​​अभिविन्यास ध्वज को पढ़ने के लिए ऊपर उल्लिखित EXIF.js लाइब्रेरी का उपयोग करता है। जानकारी के आधार पर, यह एक कैनवास तत्व का उपयोग करके रोटेशन को लागू करता है ... sandbox.juurlink.org/html5imageuploader
Rob Juurlink

यहां तक ​​कि मेरी परियोजना में बाइनरीजैक्स.जेएस को शामिल करने का प्रयास करने से एक पहुंच अस्वीकृत त्रुटि का कारण बनता है।
ओबी वान

EXIF ऑब्जेक्ट कहाँ से आता है? बाइनरीफाइल स्क्रिप्ट में इसे शामिल नहीं किया गया है, और जहाँ तक मैं बता सकता हूं, यह नियमित रूप से उपयोग किए जाने वाले jquery या किसी अन्य स्क्रिप्ट का हिस्सा नहीं है ...
jrista

6
पुस्तकालय की वेबसाइट नीचे लगती है, और केवल अन्य ExifReader पुस्तकालयों को मैंने पाया है जो ब्राउज़र समर्थन में सीमित थे। क्या कोई अच्छा विकल्प है?
प्रॉक्सिस असिलिन

19

फ़ायरफ़ॉक्स 26 का समर्थन करता है image-orientation: from-image: चित्र EXIF ​​डेटा के आधार पर चित्र या परिदृश्य प्रदर्शित किए जाते हैं। (देखें sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation ।)

क्रोम में इसे लागू करने के लिए एक बग भी है

सावधान रहें कि यह संपत्ति केवल फ़ायरफ़ॉक्स द्वारा समर्थित है और इसके पदावनत होने की संभावना है


5
बग रिपोर्ट के लिंक के लिए धन्यवाद। मैंने इसे अभिनीत किया ताकि Chrome टीम को पता हो कि अधिक लोग यही चाहते हैं।
डेमीइम्प

इस टिप्पणी के अनुसार Chromium प्रोजेक्ट सदस्य द्वारा bugs.chromium.org/p/chromium/issues/detail?id=158753#c104 : "परिवर्तन Chrome 81 में है। यह 8 में स्थिर संस्करण के रूप में जनता के लिए आएगा। -10 सप्ताह का समय "
जेफ वन

1
क्रोम पर कार्यान्वित 81 81 से शुरू होने से पहले लोगों को अपने ब्राउज़र को अपडेट करने में कुछ समय लगेगा हालांकि - कैनिअस
रॉबिन मेट्रल

11

https://github.com/blueimp/JavaScript-Load-Image एक आधुनिक जावास्क्रिप्ट लाइब्रेरी है जो न केवल एक्सिफ ओरिएंटेशन फ्लैग को निकाल सकती है - यह क्लाइंट साइड पर JPEG इमेज को सही ढंग से मिरर / रोटेट भी कर सकती है।

मैंने इस लाइब्रेरी के साथ एक ही समस्या हल की है: JS क्लाइंट-साइड एक्सिफ़ ओरिएंटेशन: रोटेट और मिरर JPEG Images


4

यदि आप इसे क्रॉस-ब्राउज़र चाहते हैं, तो आपका सबसे अच्छा दांव यह सर्वर पर करना है। आपके पास एक एपीआई हो सकता है जो एक फ़ाइल URL लेता है और आपको EXIF ​​डेटा लौटाता है; PHP के लिए एक मॉड्यूल है

यह अजाक्स का उपयोग किया जा सकता है ताकि यह उपयोगकर्ता के लिए सहज हो। यदि आप क्रॉस-ब्राउज़र संगतता के बारे में परवाह नहीं करते हैं, और HTML5 फ़ाइल कार्यक्षमता पर भरोसा कर सकते हैं , तो लाइब्रेरी JsJPEGmeta पर गौर करें जो आपको मूल जावास्क्रिप्ट में उस डेटा को प्राप्त करने की अनुमति देगा।


21
@ मिक्कोकोतामा: आपको यह समझने की जरूरत है कि स्टैक ओवरफ्लो हर किसी के लिए सवालों का जवाब देता है , बस मूल व्यक्ति इसे पूछ रहा है। अगला व्यक्ति जिसके पास एक ही उद्देश्य है जैसे कि आप एक PHP डेवलपर हो सकते हैं - आप उन्हें उन सूचनाओं से क्यों इनकार करना चाहेंगे जो Xeon04 शामिल हैं? इसे बाहर संपादित करना अनुचित था, सिर्फ इसलिए कि आप एक PHP समाधान नहीं चाहते हैं।
जॉन स्कीट

5
सवाल कहता है "जावास्क्रिप्ट में" इसलिए यह हिस्सा अप्रासंगिक था। साइट पर पहले से ही PHP के लिए कई अन्य समान प्रश्न और उत्तर हैं और इस प्रश्न के बारे में अनावश्यक शोर है।
मिको ओकटामा

2
यदि लोग जावास्क्रिप्ट समाधान के लिए पूछते हैं तो वे PHP समाधान को पहली पोस्ट के रूप में नहीं देखना चाहते हैं।
मिको ओक्टामा 14

1
@ मिकोकोहटामा ऐसा प्रतीत होता है कि आप इससे सबसे ज्यादा असहमत हैं। meta.stackexchange.com/questions/157338/… आपको लगता है कि आपके सवालों के जवाबों पर कुछ गलत स्वामित्व की भावना है।
एलेक्स टर्पिन

1
मैंने शुरुआत में सही उत्तर देने के लिए उत्तर को संपादित किया। फ़ज़ के लिए क्षमा करें।
मिको ओकटामा

3

एक मॉड्यूल की जाँच करें जो मैंने लिखा है (आप इसे ब्राउज़र में उपयोग कर सकते हैं) जो एक्सफ़ ओरिएंटेशन को CSS ट्रांस्फ़ॉर्म में परिवर्तित करता है: https://github.com/Sobesednik/exif2css

सभी नोड्स के साथ JPEG फिक्स्चर उत्पन्न करने के लिए यह नोड प्रोग्राम भी है: https://github.com/Sobesednik/generate-exif-fixtures


1
अच्छा मॉड्यूल! हालाँकि, यह पहली जगह में JPEG से EXIF ​​जानकारी कैसे प्राप्त करता है?
मिको ओक्टामा

@ मिकोकोहमा धन्यवाद और नाह यह नहीं करता है, आपको इसे एक्सिफ-जेएस या एक्सफ़िल्टोल सर्वर-साइड के साथ करना होगा
zavr

यह उपयोगी है। लेकिन यह मुझे ऐसा लगता है कि यह केवल चित्र फ़ोटो के लिए सही ढंग से काम करता है, न कि लैंडस्केप के लिए।
श्रीधर सरनोबत

3

मैं HTML पर एंड्रॉइड कैमरा द्वारा फोटो दिखाने के लिए विस्तार कोड अपलोड करता हूं, जो कि सही rotaion के साथ कुछ img टैग पर सामान्य है, विशेष रूप से img टैग के लिए जिसकी चौड़ाई ऊंचाई से अधिक है। मुझे पता है कि यह कोड बदसूरत है, लेकिन आपको किसी अन्य पैकेज को स्थापित करने की आवश्यकता नहीं है। (मैंने एक्सिफ रोटेशन मूल्य प्राप्त करने के लिए कोड से ऊपर का उपयोग किया, धन्यवाद।)

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]);

});

2

पहले से अली के जवाब में अधिक कार्यक्षमता जोड़ना / सुधारना, मैंने टाइपस्क्रिप्ट में एक उपयोग विधि बनाई जो इस मुद्दे के लिए मेरी आवश्यकताओं के अनुकूल है। यह संस्करण डिग्री में घूमता है जिसे आपको अपनी परियोजना की आवश्यकता भी हो सकती है।

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 });
  });
};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.