EXIF format (or JPEG, you can see the wiki explanation, for orientation problem, it is called EXIF format and JFIF distinguish) with orientation information in the picture (that is, the picture may actually rotate 90 or 180 degrees or 270 degrees). If the digital device is stored in EXIF format, even if the device rotates 90 or 180 degrees or 270 degrees during shooting, the picture will still look "positive" when opened with an image viewer that supports orientation information processing.
Under Linux, you can use the file command to see if the picture is in EXIF format, such as 2.jpeg, which is in EXIF format.
➜ Downloads file 2.jpeg 2.jpeg: JPEG image data, EXIF standard ➜ Downloads file 2_decode.jpeg 2_decode.jpeg: JPEG image data, JFIF standard 1.0
opencv 2.4.13 and previous 2.4.xx versions, either imread or imdecode, did not process orientation information. In version 3.2, imread correctly handles orientation information, but imdecode does not process orientation information. To enable imdecode to process orientation information correctly, either change the open CV source code, or first imwrite the image to disk and imread it again (imread correctly handles orientation information, imwrite has become JFIF format, there is no orientation information), and have not yet gone to github to confirm why the behavior of imread and imdecode is not uniform.--
Looking at the imread source code, you see that an ApplyExifOrientation function handles orientation information.
static void ApplyExifOrientation(const String& filename, Mat& img)
In version 3.2, imread source code:
static void ApplyExifOrientation(const String& filename, Mat& img) { int orientation = IMAGE_ORIENTATION_TL; if (filename.size() > 0) { ExifReader reader( filename ); if( reader.parse() ) { ExifEntry_t entry = reader.getTag( ORIENTATION ); if (entry.tag != INVALID_TAG) { orientation = entry.field_u16; //orientation is unsigned short, so check field_u16 } } } switch( orientation ) { case IMAGE_ORIENTATION_TL: //0th row == visual top, 0th column == visual left-hand side //do nothing, the image already has proper orientation break; case IMAGE_ORIENTATION_TR: //0th row == visual top, 0th column == visual right-hand side flip(img, img, 1); //flip horizontally break; case IMAGE_ORIENTATION_BR: //0th row == visual bottom, 0th column == visual right-hand side flip(img, img, -1);//flip both horizontally and vertically break; case IMAGE_ORIENTATION_BL: //0th row == visual bottom, 0th column == visual left-hand side flip(img, img, 0); //flip vertically break; case IMAGE_ORIENTATION_LT: //0th row == visual left-hand side, 0th column == visual top transpose(img, img); break; case IMAGE_ORIENTATION_RT: //0th row == visual right-hand side, 0th column == visual top transpose(img, img); flip(img, img, 1); //flip horizontally break; case IMAGE_ORIENTATION_RB: //0th row == visual right-hand side, 0th column == visual bottom transpose(img, img); flip(img, img, -1); //flip both horizontally and vertically break; case IMAGE_ORIENTATION_LB: //0th row == visual left-hand side, 0th column == visual bottom transpose(img, img); flip(img, img, 0); //flip vertically break; default: //by default the image read has normal (JPEG_ORIENTATION_TL) orientation break; } } /** * Read an image * * This function merely calls the actual implementation above and returns itself. * * @param[in] filename File to load * @param[in] flags Flags you wish to set. */ Mat imread( const String& filename, int flags ) { /// create the basic container Mat img; /// load the data imread_( filename, flags, LOAD_MAT, &img ); /// optionally rotate the data if EXIF' orientation flag says so if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) { ApplyExifOrientation(filename, img); } /// return a reference to the data return img; }
3.2 imdecode source code:
static void* imdecode_( const Mat& buf, int flags, int hdrtype, Mat* mat=0 ) { CV_Assert(!buf.empty() && buf.isContinuous()); IplImage* image = 0; CvMat *matrix = 0; Mat temp, *data = &temp; String filename; ImageDecoder decoder = findDecoder(buf); if( !decoder ) return 0; if( !decoder->setSource(buf) ) { filename = tempfile(); FILE* f = fopen( filename.c_str(), "wb" ); if( !f ) return 0; size_t bufSize = buf.cols*buf.rows*buf.elemSize(); fwrite( buf.ptr(), 1, bufSize, f ); fclose(f); decoder->setSource(filename); } if( !decoder->readHeader() ) { decoder.release(); if ( !filename.empty() ) { if ( remove(filename.c_str()) != 0 ) { CV_Error( CV_StsError, "unable to remove temporary file" ); } } return 0; } CvSize size; size.width = decoder->width(); size.height = decoder->height(); int type = decoder->type(); if( (flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED ) { if( (flags & CV_LOAD_IMAGE_ANYDEPTH) == 0 ) type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); if( (flags & CV_LOAD_IMAGE_COLOR) != 0 || ((flags & CV_LOAD_IMAGE_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) ) type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); else type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); } if( hdrtype == LOAD_CVMAT || hdrtype == LOAD_MAT ) { if( hdrtype == LOAD_CVMAT ) { matrix = cvCreateMat( size.height, size.width, type ); temp = cvarrToMat(matrix); } else { mat->create( size.height, size.width, type ); data = mat; } } else { image = cvCreateImage( size, cvIplDepth(type), CV_MAT_CN(type) ); temp = cvarrToMat(image); } bool code = decoder->readData( *data ); decoder.release(); if ( !filename.empty() ) { if ( remove(filename.c_str()) != 0 ) { CV_Error( CV_StsError, "unable to remove temporary file" ); } } if( !code ) { cvReleaseImage( &image ); cvReleaseMat( &matrix ); if( mat ) mat->release(); return 0; } return hdrtype == LOAD_CVMAT ? (void*)matrix : hdrtype == LOAD_IMAGE ? (void*)image : (void*)mat; } Mat imdecode( InputArray _buf, int flags ) { Mat buf = _buf.getMat(), img; imdecode_( buf, flags, LOAD_MAT, &img ); return img; }