Skip to main content
New Participant
July 9, 2011
Question

Camera image orientation on iOS

  • July 9, 2011
  • 3 replies
  • 23510 views

Hi,

Taking a picture using CamerUI on iOS (and on Android) may bring the image oriented in the wrong angle.

Reading the Exif on Android is fine and allows reorientation to the correct position, however this is not working on iOS.

Since I assume taking a picture using the camera is a core feature for many applications, I’d appreciate if someone who managed to solve it can post a solution here?

Thanks!

    This topic has been closed for replies.

    3 replies

    Known Participant
    October 20, 2015

    The cameraRoll orientation issue is now a bug in bugbase. Upvote if you need a fix. Bug#4070057 - CameraRoll on iOS returns Bitmap in incorrect orientation

    Participating Frequently
    May 15, 2012

    Well, is there any solutions? I still can't fix the camera orientation. I already unchecked Auto orientation in publish settings and tried StageOrientationChange, but no helps.

    That's weird when loadFilePromise doesn't get the correct orrientation of the captured photo.

    Participating Frequently
    May 16, 2012

    GoonNguyen,

    The root of the trouble is that there are two ways to store image orientation information in the JPEG file. In my (admittedly less than comprehensive) experience, most cameras and applications rotate the image data within the file so that the top-left-hand corner of the stored image array is the top-left-hand corner of the picture. In other words, if you took two pictures with the phone camera in opposite orientations, the top left of the picture is stored in the same place in the JPEG file for both.

    On iOS they chose a different way. The image data is stored as it comes out of the sensor and an additional exif data item is added to indicate the orientation. Thus if you took the same two pictures as above, you would find that one of them is inverted in the JPEG file compared to the other. So on iOS, you have to read the EXIF data to tell which way is up. (And reading the EXIF data is problematic since the file isn't really a proper JPEG format, it is a JFIF or some combination of the two. JFIF and JPEG are technically incompatible, though the practical differences are minor.)

    Participating Frequently
    May 17, 2012

    Thank you a lot for you quick reply. After few hours trying and looking around, finnally I can read EXIF of a photo on iOS, actually it's JFIF as you said.

    Allow me to write something here, hope it helps people who still can't get the orientation of the photos on iOS...

    As first, you guys have to take a look at this: http://code.shichiseki.jp/as3/ExifInfo

    And as Joe said above, On iOS, the datasource for the MediaPromise is not file-based, so the file URL is null. Instead, you have to read the image data into a ByteArray in order to access the raw image data. (To just display the image, you could use the Loader.loadMediaPromise() method, but that doesn't give you the EXIF data.)

    Also:

    It also wasn't hard to fix the jp.shichiseki library to ignore the JFIF marker. You just have to add the folowing to the ExifInfo class:

    private const JFIF_MAKER:Array = [0xff, 0xe0]; //new marker type

            //Updated to skip JFIF marker
            private function validate(stream:ByteArray):Boolean {
                var app1DataSize:uint;
                // JPG format check
                if (!hasSoiMaker(stream) ) {
                    return false;
                }
                if(hasJFIFMaker(stream)) //Skip the JFIF marker, if present. CWW
                {
                    stream.position += 16;
                }
                else stream.position -=2; //Set position back to start of APP1 marker
               
                if ( !hasAPP1Maker(stream)) {
                    return false;
                }
                // handle app1 data size
                app1DataSize = stream.readUnsignedShort();
                if (!hasExifHeader(stream)) {
                    return false;
                }
                return true;
            }

            //New function to check for JFIF marker
            private function hasJFIFMaker(stream:ByteArray):Boolean {
                return compareStreamBytes(stream, JFIF_MAKER);
            }

    Note that the library fails silently if it doesn't recognize the file format. That's why you get a null reference error. The library wasn't creating any of its usual objects. Another thing to be aware of is that not all devices on Android record the orientation. In my collection, only one of three did so.

    Finally, below is my few lines of code:

    var mediaPromise:MediaPromise;

    var dataSource:IDataInput;

    var loader:Loader = new Loader();

    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);

    addChild(loader);

    captureBtn.addEventListener(MouseEvent.CLICK, function(){

              trace(CameraRoll.supportsBrowseForImage)

              if(CameraRoll.supportsBrowseForImage){

                        camRoll.browseForImage()

              }

    })

    function onSelected(e:MediaEvent){

              mediaPromise = e.data;

              dataSource = mediaPromise.open();

              if( mediaPromise.isAsync )

        {

            trace( "Asynchronous media promise." );

            var eventSource:IEventDispatcher = dataSource as IEventDispatcher;           

            eventSource.addEventListener( Event.COMPLETE, onMediaLoaded );        

        }

        else

        {

            trace( "Synchronous media promise." );

            readMediaData();

        }

              //output.appendText("\n"+mediaPromise)

              //output.appendText("\n"+mediaPromise.file)

              //output.appendText("\n"+mediaPromise.file.url)

              //trace(mediaPromise.file.url);

    }

    function onMediaLoaded( event:Event ):void

    {

        trace("Media load complete");

        readMediaData();

    }

    function readMediaData():void

    {

              // THIS PART IS READING JFIF DATA:

              var data:ByteArray = new ByteArray();

              dataSource.readBytes( data );

              exif = new ExifInfo(data);

              //output.text = displayIFD(exif.ifds.exif); // This stores some properties like resolutionX, resolutionY,.etc.

              //output.text = displayIFD(exif.ifds.primary); // This one stores the orientation data.

              output.text = getOrientation(exif.ifds.primary)

        //do something with the data

              loader.loadFilePromise(mediaPromise);

    }

    function displayIFD(ifd:IFD):String {

              //trace(" --- " + ifd.level + " --- ");

              var str:String = "";

              for (var entry:String in ifd) {

                        trace(entry + ": " + ifd[entry]);

                        str += (entry + ": " + ifd[entry] + "\n")

              }

              return str;

    }

    function getOrientation(ifd:IFD):String{

              var str:String = "";

              for (var entry:String in ifd) {

                        if(entry == "Orientation"){

                                  str = ifd[entry];

                        }

              }

              switch(str){

                        case "1": //normal

                                  str = "NORMAL";

                        break;

                        case "3": //rotated 180 degrees (upside down)

                                  str = "UPSIDE_DOWN";

                        break;

                        case "6": //rotated 90 degrees CW

                                  str = "ROTATED_LEFT"

                        break;

                        case "8": //rotated 90 degrees CCW

                                  str = "ROTATED_RIGHT"

                        break;

                        case "9": //unknown

                                  str = "UNKNOWN"

                        break;

              }

              return str;

    }

    function onCancelled(e:Event){

    }

    Thank you a bunch, again, Joe!

    Have a good day!

    Participating Frequently
    July 20, 2011

    Up !

    Same problem for me.

    How a basic fonction like this is so difficult to find !?

    Did you find anything yet ?

    Cheers.

    Participating Frequently
    July 20, 2011

    This information comes from one of Adobe's quality engineers:

    So I found that the image that we get after using CameraRoll/CameraUI  does contain EXIF information but not in the format expected by the  ExifInfo AS3 library (http://code.shichiseki.jp/as3/ExifInfo/).

    Firstly, it does contain the orientation information which I am able to  verify using the Jpeg decoding tool :  http://www.impulseadventure.com/photo/jpeg-snoop-source.html.

    The Exif data is stored in one of JPEG’s defined utility Application  Segments APPn. The AS3 library - ExifInfo that you are using for reading  Exif header expects APP1 marker whereas the actual image has APP0 at  that place followed by APP1 segment which contains the orientation data.  Images that comply to JFIF standards have APP0 marker just after SOI  (Start of Image) and images that comply to EXIF standards should have  APP1 marker after SOI.

    As per wiki http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format :  "many programs and digital cameras produce files with both application  segments included" which is what exactly happens in our case.

    So I think you may try switching to a library which is able to parse an  image that has multiple APPn markers and read only the one required. You  can even modify the ExifInfo library.

    Hope this helps.

    Participating Frequently
    July 21, 2011

    Thanks a lot for your answer.

    I understand the concept of switching the APP marker to get data, but i already have trouble about accessing picture.

    For exemple if i take the sample from ExifInfo AS3 Library (just the part about accessing the image), here is my test

    private function mediaSelectHandler( event:MediaEvent ):void
    {
            mediaPromise = event.data;
            trace(mediaPromise);
            trace(mediaPromise.file.url);

            /*
            this.exifLoader = new ExifLoader();
            this.exifLoader.addEventListener(Event.COMPLETE, completeHandler );
            this.exifLoader.load( new URLRequest( mediaPromise.file.url ) );

            */

    }

    When media is selected, when i trace mediaPromise, i get a media [mediaPromise object], but when i trace mediaPromise.file.url its null. In that way how can i get access to the file because since on iOS its null.

    Is there any example/sample about this, because i really get the concept and the logic, but i have no idea how to get it.

    Thank you again