LoginSignup
0
0

More than 5 years have passed since last update.

OpenCV 2.4.11でJPEG画像を自動回転する

Last updated at Posted at 2015-08-12

OpenCVでJPEGの自動回転できるのか、という宿題がでたので、回答を。あ、大部分はlibjpegのマクロ使っていますのでので。

使い方は、
imread(filename , 128 + 1);とかimread(filename , 128 + 3);ですね。

flagsに-1を指定する事もできるはずですが、まあJPEG画像だから気にしない(PNG画像ならば透過チャネルも読めるはず)。

あとは90度単位の回転がもっと速いロジックになっていればなあ……と嘆くばかりです。

patch.patch
anobiidae@anobiidae-virtual-machine:~/opencv2$ diff opencv-2.4.11/modules/highgui/src opencv-2.4.11.auto/modules/highgui/src/ -c
共通のサブディレクトリー: opencv-2.4.11/modules/highgui/src/files_Qt  opencv-2.4.11.auto/modules/highgui/src/files_Qt
diff -c opencv-2.4.11/modules/highgui/src/grfmt_jpeg.cpp opencv-2.4.11.auto/modules/highgui/src/grfmt_jpeg.cpp
*** opencv-2.4.11/modules/highgui/src/grfmt_jpeg.cpp    2015-02-25 21:10:31.000000000 +0900
--- opencv-2.4.11.auto/modules/highgui/src/grfmt_jpeg.cpp   2015-08-13 08:32:11.492022640 +0900
***************
*** 102,107 ****
--- 102,108 ----
      jpeg_decompress_struct cinfo; // IJG JPEG codec structure
      JpegErrorMgr jerr; // error processing manager state
      JpegSource source; // memory buffer source
+     int m_orientation; // Orientation
  };

  /////////////////////// Error processing /////////////////////
***************
*** 167,172 ****
--- 168,342 ----
      longjmp( err_mgr->setjmp_buffer, 1 );
  }

+ /*
+  * Parse the DHT table.
+  * This macro comes from jpeg9a (jdmarker.c).
+  */
+ 
+ #define MAKESTMT(stuff)       do { stuff } while (0)
+ /*
+  * Macros for fetching data from the data source module.
+  *
+  * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect
+  * the current restart point; we update them only when we have reached a
+  * suitable place to restart if a suspension occurs.
+  */
+ 
+ /* Declare and initialize local copies of input pointer/count */
+ #define INPUT_VARS(cinfo)  \
+   struct jpeg_source_mgr * datasrc = (cinfo)->src;  \
+   const JOCTET * next_input_byte = datasrc->next_input_byte;  \
+   size_t bytes_in_buffer = datasrc->bytes_in_buffer
+ 
+ /* Unload the local copies --- do this only at a restart boundary */
+ #define INPUT_SYNC(cinfo)  \
+   ( datasrc->next_input_byte = next_input_byte,  \
+     datasrc->bytes_in_buffer = bytes_in_buffer )
+ 
+ /* Reload the local copies --- used only in MAKE_BYTE_AVAIL */
+ #define INPUT_RELOAD(cinfo)  \
+   ( next_input_byte = datasrc->next_input_byte,  \
+     bytes_in_buffer = datasrc->bytes_in_buffer )
+ 
+ /* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available.
+  * Note we do *not* do INPUT_SYNC before calling fill_input_buffer,
+  * but we must reload the local copies after a successful fill.
+  */
+ #define MAKE_BYTE_AVAIL(cinfo,action)  \
+   if (bytes_in_buffer == 0) {  \
+     if (! (*datasrc->fill_input_buffer) (cinfo))  \
+       { action; }  \
+     INPUT_RELOAD(cinfo);  \
+   }
+ 
+ /* Read a byte into variable V.
+  * If must suspend, take the specified action (typically "return FALSE").
+  */
+ #define INPUT_BYTE(cinfo,V,action)  \
+   MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V = GETJOCTET(*next_input_byte++); )
+ 
+ /* As above, but read two bytes interpreted as an unsigned 16-bit integer.
+  * V should be declared unsigned int or perhaps INT32.
+  */
+ #define INPUT_2BYTES(cinfo,V,action)  \
+   MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \
+         MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V += GETJOCTET(*next_input_byte++); )
+ 
+ /* As above, but read four bytes interpreted as an unsigned 32-bit integer.
+  * V should be declared unsigned int.
+  */
+ #define INPUT_4BYTES(cinfo,V,action)  \
+   MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 24; \
+         MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V += (GETJOCTET(*next_input_byte++)) << 16; \
+         MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V += (GETJOCTET(*next_input_byte++)) << 8; \
+         MAKE_BYTE_AVAIL(cinfo,action); \
+         bytes_in_buffer--; \
+         V += (GETJOCTET(*next_input_byte++)); )
+ /*
+  * Routines for processing APPn and COM markers.
+  * These are either saved in memory or discarded, per application request.
+  * APP0 and APP14 are specially checked to see if they are
+  * JFIF and Adobe markers, respectively.
+  */
+ 
+ #define APP1_DATA_LEN 10  /* Length of interesting data in APP14 */
+ 
+ #define SWAP_2BYTES(V) \
+   { V = (!isLE)?V:( ((V >> 8) & 0xff) | (V << 8 ) ); }
+ #define SWAP_4BYTES(V) \
+   { V = (!isLE)?V:( ((V >> 24) & 0xff) | ((V >> 8) & 0xff00) | ((V << 8) & 0xff0000) | (V << 24 ) ); }
+ 
+ METHODDEF(int)
+ proc_exif(j_decompress_ptr cinfo)
+ {
+   bool isLE = false;
+   INT32 length;
+   JOCTET b[APP1_DATA_LEN];
+   unsigned int i, numtoread;
+   INPUT_VARS(cinfo);
+ 
+   INPUT_2BYTES(cinfo, length, return FALSE);
+   length -= 2;
+ 
+   /* get the interesting part of the marker data */
+   if (length >= APP1_DATA_LEN)
+     numtoread = APP1_DATA_LEN;
+   else if (length > 0)
+     numtoread = (unsigned int) length;
+   else
+     numtoread = 0;
+   for (i = 0; i < numtoread; i++)
+     INPUT_BYTE(cinfo, b[i], return FALSE);
+   length -= numtoread;
+ 
+   if ( memcmp(b,"Exif\0\0",6) != 0 ) {
+     printf("Exif tag is not alive\n");
+     goto exit;
+   }
+ 
+   if ( memcmp(b+6,"MM",2) == 0 ) {
+     isLE = false;
+   }else{
+     printf("Endian is not MM\n");
+     goto exit;
+   }
+ 
+   unsigned int ifd_offset;
+  INPUT_4BYTES(cinfo, ifd_offset, return FALSE);
+  SWAP_4BYTES(ifd_offset);
+  length -= 4;
+  if(ifd_offset != 8 ) {
+    printf("IFD should be next to header");
+    goto exit;
+  }
+ 
+  {
+    int tag_num;
+    INPUT_2BYTES(cinfo, tag_num, return FALSE ); SWAP_2BYTES(tag_num);
+    length-=2;
+    for(; tag_num > 0 ; tag_num -- )
+    {
+      int tag_tag, tag_type, tag_count;
+      int tag_offset, tag_dmy; 
+ 
+      INPUT_2BYTES(cinfo, tag_tag,    return FALSE ); SWAP_2BYTES(tag_tag);
+      INPUT_2BYTES(cinfo, tag_type,   return FALSE ); SWAP_2BYTES(tag_type);
+      INPUT_4BYTES(cinfo, tag_count,  return FALSE ); SWAP_4BYTES(tag_count);
+      if(tag_type == 0x03 /* short */)
+      {
+        INPUT_2BYTES(cinfo, tag_offset, return FALSE ); SWAP_2BYTES(tag_offset);
+        INPUT_2BYTES(cinfo, tag_dmy,    return FALSE );
+      }else{
+        INPUT_4BYTES(cinfo, tag_offset, return FALSE ); SWAP_4BYTES(tag_offset);
+      }
+      if(tag_tag == 274 /* Orientation */ ){
+         JpegState* state = (JpegState*)cinfo; // HACKME!
+         state->m_orientation = tag_offset;
+      }
+      length -= 12;
+    }
+  }
+ 
+ exit:
+   /* skip any remaining data -- could be lots */
+   INPUT_SYNC(cinfo);
+   if (length > 0)
+     (*cinfo->src->skip_input_data) (cinfo, (long) length);
+ 
+   return TRUE;
+ }

  /////////////////////// JpegDecoder ///////////////////

***************
*** 220,225 ****
--- 390,396 ----
      m_state = state;
      state->cinfo.err = jpeg_std_error(&state->jerr.pub);
      state->jerr.pub.error_exit = error_exit;
+     state->m_orientation = 1; // default;

      if( setjmp( state->jerr.setjmp_buffer ) == 0 )
      {
***************
*** 240,250 ****
--- 411,423 ----

          if (state->cinfo.src != 0)
          {
+                   jpeg_set_marker_processor( &state->cinfo, JPEG_APP0+1, proc_exif );
              jpeg_read_header( &state->cinfo, TRUE );

              m_width = state->cinfo.image_width;
              m_height = state->cinfo.image_height;
              m_type = state->cinfo.num_components > 1 ? CV_8UC3 : CV_8UC1;
+             m_orientation = state->m_orientation;
              result = true;
          }
      }
***************
*** 450,455 ****
--- 623,630 ----
                                                JPOOL_IMAGE, m_width*4, 1 );

              uchar* data = img.data;
+             if(*data == 0){ m_orientation = 1; }
+ 
              for( ; m_height--; data += step )
              {
                  jpeg_read_scanlines( cinfo, buffer, 1 );
***************
*** 470,475 ****
--- 645,678 ----
              }
              result = true;
              jpeg_finish_decompress( cinfo );
+ 
+             {
+               struct {
+                 bool isFlip;
+                 int  rotateAngle;
+               } tLUT[] = {
+                 {false,0},
+                 {false,  0},{true ,  0},{false,180},{true ,180},
+                 {true , 90},{false, 90},{true ,270},{false ,270},
+               };
+ 
+               printf("FLAG=%d\n", m_orientation );
+ 
+               if(tLUT[m_orientation].rotateAngle == 90){
+                 transpose(img,img);
+                 flip(img,img, 1);
+               }
+               if(tLUT[m_orientation].rotateAngle == 180){
+                 flip(img,img,-1);
+               }
+               if(tLUT[m_orientation].rotateAngle == 270){
+                 transpose(img, img);
+                 flip(img,img,0);
+               }
+               if(tLUT[m_orientation].isFlip == true ){
+                 flip(img,img, 1);
+               }
+             }
          }
      }

diff -c opencv-2.4.11/modules/highgui/src/grfmt_jpeg.hpp opencv-2.4.11.auto/modules/highgui/src/grfmt_jpeg.hpp
*** opencv-2.4.11/modules/highgui/src/grfmt_jpeg.hpp    2015-02-25 21:10:31.000000000 +0900
--- opencv-2.4.11.auto/modules/highgui/src/grfmt_jpeg.hpp   2015-08-13 07:23:17.984141888 +0900
***************
*** 67,75 ****
      ImageDecoder newDecoder() const;

  protected:
- 
      FILE* m_f;
      void* m_state;
  };


--- 67,75 ----
      ImageDecoder newDecoder() const;

  protected:
      FILE* m_f;
      void* m_state;
+     int   m_orientation;
  };


diff -c opencv-2.4.11/modules/highgui/src/loadsave.cpp opencv-2.4.11.auto/modules/highgui/src/loadsave.cpp
*** opencv-2.4.11/modules/highgui/src/loadsave.cpp  2015-02-25 21:10:31.000000000 +0900
--- opencv-2.4.11.auto/modules/highgui/src/loadsave.cpp 2015-08-13 08:29:48.204026773 +0900
***************
*** 242,247 ****
--- 242,250 ----
          temp = cvarrToMat(image);
      }

+     // Auto-rotation flag is stored at first bytes
+     data->at<unsigned char>(0,0) = flags >> 7; 
+ 
      if( !decoder->readData( *data ))
      {
          cvReleaseImage( &image );
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0