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