1
votes

There is a strange change when saving JPEG image to the iOS photo library. I don't know if I am doing something wrong. I'm using libjpeg-turbo to access JPEG images and then I modify DCT coefficients of the image. Modified image (just in DCT, nothing else) is saved in photo library. But after I open the saved image, DCT coefficients are not the same that I changed in the previous step.

In detail, let me explain how I'm adding +1 to each DCT. I am using standard procedure from "example.c" of the libjpeg library:

struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
FILE * infile;

if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
}

cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;

if (setjmp(jerr.setjmp_buffer)) {
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
}

jpeg_create_decompress(&cinfo);

jpeg_stdio_src(&cinfo, infile);

(void) jpeg_read_header(&cinfo, TRUE);

jvirt_barray_ptr* coeffs_array;
coeffs_array = jpeg_read_coefficients(&cinfo);

BOOL done = FALSE;
for (int ci = 0; ci < 3; ci++)
{
    JBLOCKARRAY buffer_one;
    JCOEFPTR blockptr_one;
    jpeg_component_info* compptr_one;
    compptr_one = cinfo.comp_info + ci;

    for (int by = 0; by < compptr_one->height_in_blocks; by++)
    {
        buffer_one = (cinfo.mem->access_virt_barray)((j_common_ptr)&cinfo, coeffs_array[ci], by, (JDIMENSION)1, FALSE);

        for (int bx = 0; bx < compptr_one->width_in_blocks; bx++)
        {
            blockptr_one = buffer_one[0][bx];

            for (int bi = 0; bi < 64; bi++)
            {
                blockptr_one[bi]++;
            }                  
        }   
    } 
}

write_jpeg(output, &cinfo, coeffs_array); // saving modified JPEG to the output file

jpeg_destroy_decompress(&cinfo);
fclose(infile);

After this I have a new JPEG image saved in the file, lets say "new.jpg". Now I want to save this "new.jpg" to the photo library so I load the image by:

imageToSave = [UIImage imageWithContentsOfFile:outputFile];

I also checked that the DCT coefficients remained changed. After I have the same image wuth my modified DCT checked then I'm going to save it:

UIImageWriteToSavedPhotosAlbum(imageToSave, nil, nil, nil);

Image "new.jpg" is saved in photo library now.

Until now, everything works great, DCT coefficients works like it was meant with the libjpeg library. The change comes when I load the saved image again and look into DCT coefficients. I found out that DCTs are changed and I don't know why. Is there any optimization algorithm that iOS use when you want to save JPEG image? Why are DCTs changed.

I'm using the following procedure to read the saved image:

NSData *jpegData = UIImageJPEGRepresentation([info objectForKey:UIImagePickerControllerOriginalImage], 1);
[jpegData writeToFile:file atomically:YES]; // saved image is saved in file and I can use the procedure from above to check DCTs

Furthermore, this is an example of original DCTs, modified DCTs by adding +1 to all, and loaded DCTs after saving it to photo library for one 8x8 block:

Original DCTs:
-759 -24 -8 1 -1 0 0 1 
56 -27 -10 1 0 1 0 0 
8 0 0 0 0 -1 0 0 
0 0 0 -1 0 -1 0 -1 
0 0 0 1 0 0 0 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 0 0 0 
0 0 0 -1 0 0 0 0 

Modified DCTs by libjpeg:
-758 -23 -7 2 0 1 1 2 
57 -26 -9 2 1 2 1 1 
9 1 1 1 1 0 1 1 
1 1 1 0 1 0 1 0 
1 1 1 2 1 1 1 1 
1 1 1 2 1 1 1 1 
1 1 1 1 1 1 1 1 
1 1 1 0 1 1 1 1 

DCTs after saving JPEG to photo library:
-758 -22 -7 2 0 0 0 0 
58 -26 -8 3 0 0 0 0 
8 2 0 0 -1 1 0 0 
2 3 0 0 0 0 0 0 
2 1 -1 0 0 0 0 0 
0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 -1 

Any help would be appreciated. I am really out of ideas why are DCTs after saving image to photo library altered.

2

2 Answers

2
votes

iOS will always recompress your JPEG when you use UIImage and its methods, hence altering DCT. Instead, use AssetsLibrary to store and retrieve user's pictures as NSData. This behavior is not documented, but it works.

I'd also recommend storing the .jpeg in a temp folder, and then feeding it to libjpeg-turbo.

-1
votes

You should definitively simplify your code to see where the unwanted behaviour occurs. You're doing at least five different steps where things can go wrong.

Now, that been said, not working on iOS but knowing JPEG codec, be careful of the quality you used to save your file.jpg. I'm not sure what is the default for your specific library, but if not 100%, it will definitively affect your DCT coefficients.

Try saving your modified image using a lossless codec such as PNG or BMP.