Ben
Ben

Reputation: 4666

How can I catch corrupt JPEGs when loading an image with imread() in OpenCV?

OpenCV says something like

Corrupt JPEG data: premature end of data segment

or

Corrupt JPEG data: bad Huffman code

or

Corrupt JPEG data: 22 extraneous bytes before marker 0xd9

when loading a corrupt jpeg image with imread(). Can I somehow catch that? Why would I get this information otherwise? Do I have to check the binary file on my own?

Upvotes: 25

Views: 29010

Answers (11)

Hamzah Al-Qadasi
Hamzah Al-Qadasi

Reputation: 9786

We read each JPEG image in the specified folder using OpenCV and overwrite it by re-saving with a quality setting of 95. This re-encoding process fixes any corruption issues by standardizing the image data format.

for filename in tqdm(image_files, desc="Re-encoding images"):

    image_path = os.path.join(folder_path, filename)
    img = cv2.imread(image_path)

    if img is not None:
       # Re-encode and overwrite the image
       cv2.imwrite(image_path, img, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
    else:
        print(f"Failed to read image data: {image_path}")

This solution fixed my problem after a long time of debugging.

Upvotes: 0

Mirronelli
Mirronelli

Reputation: 780

I found an easy solution without the need to recompile openCV. You can use imagemagick to detect the same errors, however it returns an error as expected. See the description here: https://stackoverflow.com/a/66283167/2887398

Upvotes: 0

Kanaris007
Kanaris007

Reputation: 314

I found that the issue is in libjpeg. If OpenCV uses it, it gets error

Corrupt JPEG data: 22 extraneous bytes before marker 0xd9

You can try my solution to solve it. It disables JPEG during compilation. After that OpenCV cannot read/write, but it works.

cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_SHARED_LIBS=OFF -D BUILD_EXAMPLES=OFF -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D WITH_JPEG=OFF -D WITH_IPP=OFF  ..

Upvotes: 0

Ender
Ender

Reputation: 25

If you load your image with imdecode, you can check errno :

  std::vector<char> datas();
  //Load yout image in datas here
  errno = 0;
  cv::Mat mat = cv::imdecode(datas, -1);
  if (errno != 0)
  {
    //Error
  }

(tested on OpenCV 3.4.1)

Upvotes: 0

Santhosh
Santhosh

Reputation: 1672

Any one stumbles upon this post and reads this answer.

I had to get hold of a corrupted image file.

These websites can help you corrupt your file

First and the third website was not that much useful.

Second website is interesting as I could set the amount of file that I need to corrupt.

OpenCV version I used here is 3.4.0

I used normal cv2.imread(fileLocation)

fileLocation Location of corrupted image file

OpenCV didn't show any error message for any of the corrupted files used here

First and Third website only gave one file and both had None stored in them, when I tried to print them

Second website did let me decide the amount of file that was needed to be corrupted

Corruption% Opencv message on printing the image

4% None

10% None

25% None

50% None Corrupt JPEG data: 3 extraneous bytes before marker 0x4f

75% None Corrupt JPEG data: 153 extraneous bytes before marker 0xb2

100% Corrupt JPEG data: 330 extraneous bytes before marker 0xc6 None

I guess the only check we have to make here would be

if image is not None: Do your code or else pop an error

Upvotes: 1

jdhao
jdhao

Reputation: 28329

I am using opencv python package to read some image and also met this error message. This error can not be catch by Python. But if you want to find which image is corrupted without recompiling opencv as @Robbert suggested, you can try the following method.

First, you can pinpoint the directory where the corrupt images reside, which is fairly easy. Then you go to the directory, and use mogrify command line tool provided by ImageMagick to change the image meta info, as suggest by @goe.

mogrify -set comment "errors fixed in meta info" -format png *.jpg

The above command will convert the original jpg image to png format and also clean the original image to remove errors in meta info. When you run mogrify command, it will also output some message about which image is corrupted in the directory so that you can accurately find the corrupted image.

After that, you can do whatever you want with the original corrupted jpg image.

Upvotes: 1

goe
goe

Reputation: 2303

It could be easier to fix the error in the file instead of trying to repair the loading function of OpenCV. If you are using Linux you can use ImageMagick to make reparation to a set of images (is usual to have it installed by default):

$ mogrify -set comment 'Image rewritten with ImageMagick' *.jpg

This command changes a property of the file leaving the image data untouched. However, the image is loaded and resaved, eliminating the extra information that causes the corruption error.

If you need more information about ImageMagick you can visit their website: http://www.imagemagick.org/script/index.php

Upvotes: 5

user2921336
user2921336

Reputation: 9

You can redirect stderr to a file, then after imread, search for the string "Huffman" inside that file. After searching the file, empty it. It works for me and now I am able to discard corrupted images and just process good ones.

Upvotes: 0

Robbert
Robbert

Reputation: 5193

OpenCV (version 2.4) does not overwrite the basic error handling for libjpeg, making them 'uncatchable'. Add the following method to modules/highgui/src/grfmt_jpeg.cpp, right below the definition of error_exit():

METHODDEF(void)
output_message( j_common_ptr cinfo )
{
    char buffer[JMSG_LENGTH_MAX];

    /* Create the message */
    (*cinfo->err->format_message) (cinfo, buffer);

    /* Default OpenCV error handling instead of print */
    CV_Error(CV_StsError, buffer);
}

Now apply the method to the decoder error handler:

state->cinfo.err = jpeg_std_error(&state->jerr.pub);
state->jerr.pub.error_exit = error_exit;
state->jerr.pub.output_message = output_message; /* Add this line */

Apply the method to the encoder error handler as well:

cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = error_exit;
jerr.pub.output_message = output_message; /* Add this line */

Recompile and install OpenCV as usual. From now on you should be able to catch libjpeg errors like any other OpenCV error. Example:

>>> cv2.imread("/var/opencv/bad_image.jpg")
OpenCV Error: Unspecified error (Corrupt JPEG data: 1137 extraneous bytes before marker 0xc4) in output_message, file /var/opencv/opencv-2.4.9/modules/highgui/src/grfmt_jpeg.cpp, line 180
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cv2.error: /var/opencv/opencv-2.4.9/modules/highgui/src/grfmt_jpeg.cpp:180: error: (-2) Corrupt JPEG data: 1137 extraneous bytes before marker 0xc4 in function output_message

(I've submitted a pull request for the above but it got rejected because it would cause issues with people reading images without exception catching.)

Hope this helps anyone still struggling with this issue. Good luck.

Upvotes: 12

Zaw Lin
Zaw Lin

Reputation: 5708

i had to deal with this recently and found a solution over here

http://artax.karlin.mff.cuni.cz/~isa_j1am/other/opencv/

i just need to make 2 edits @ $cv\modules\highgui\src\grfmt_jpeg.cpp.

--- opencv-1.0.0.orig/otherlibs/highgui/grfmt_jpeg.cpp  2006-10-16 13:02:49.000000000 +0200
+++ opencv-1.0.0/otherlibs/highgui/grfmt_jpeg.cpp   2007-08-11 09:10:28.000000000 +0200
@@ -181,7 +181,7 @@
             m_height = cinfo->image_height;
             m_iscolor = cinfo->num_components > 1;

-            result = true;
+            result = (cinfo->err->num_warnings == 0);
         }
     }

@@ -405,8 +405,9 @@
                         icvCvt_CMYK2Gray_8u_C4C1R( buffer[0], 0, data, 0, cvSize(m_width,1) );
                 }
             }
-            result = true;
+
             jpeg_finish_decompress( cinfo );
+            result = (cinfo->err->num_warnings == 0);
         }
     }

Upvotes: 2

ypnos
ypnos

Reputation: 52317

You cannot catch it if you use imread(). However there is imdecode() function that is called by imread(). Maybe it gives you more feedback. For this you would have to load the image into memory on your own and then call the decoder.

It boils down to: You have to dig through the OpenCV sources to solve your problem.

Upvotes: 4

Related Questions