New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Saved .mpo are not compatible with Fujifilm W3 cameras #6720
Comments
Hi. Without having access to a Fujifilm camera, the only way to verify a fix for this is to check with you, so this will involve trial and error. My first attempt will be changing the MP Image Type from Undefined to Multi-frame Disparity, like your original image - |
Hello Andrew
Thanks for your quick response
I am away from my pc for a couple of days. I will check when I am back.
Kind regards
Frank
|
Hello Andrew,
It does not work. There is still a loading error.
I've dug a little bit in the differences between the two files and there are many. I've written a little tool to parse JPEG files (I imagine you have much better tools yourself)
Here are my findings :
Original MPO file
Pillow saved MPO file
Image 1
Has APP1 EXIF
No APP0 JFIF
APP2 MPF has 5 fields
MPF version present
Other MPF fields seem less important
Image 1
no APP1
APP0 JFIF
APP2 MPF has 2 fields
MPF version missing
Image 2
Has APP2 MPF
Image 2
Has no MPF
I've put in red the most important differences.
* Would it be possible to test a Pillow output with
* No JFIF segment
* An Exif segment
* An MPF segment for the first image that contains the MPF Version field
* Thanks in advance
Frank
…------------------------------
More detailed analysis first JPEG is the original file. Second JPEG is the output of PILLOW.
-----Start JPEG analyse : 414466 bytes
** SOI
** APP1
length= 7594
Exif
in parse_identifier_segment
new index 7596
** EOI at 7626
new index 7627
** APP2
length= 190
MPF
in parse_identifier_segment
MP_endian b'II*\x00' little
offset 8
count 3
MPFVersionTagID 45056 b'\x00\xb0'
MPFVersion type b'\x07\x00'
count for MPFVersionTagID 4
MPF Version b'0100'
NumberOfImageTagID 45057 b'\x01\xb0'
numberOfImage type= b'\x04\x00'
count for NumberOfImageTagID 1
nb_of_images = 2
MPEntryTagID 45058 b'\x02\xb0'
MPEntry type b'\x07\x00'
count for MPEntryTagID 32
image 1 : b"\x00\x002\x00\x00\x00R\x00\x00\x00\x02\x00\x02 \x80'"
image 1 : attribute=3276800 size=5373952 offset=131072 dep1=8194 dep2=10112 image 2 : b'\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x00\x82+'
image 2 : attribute=3 size=0 offset=131072 dep1=2 dep2=11138 end 3rd field field? b'\x03\x00\xad\t\x03\x00\x00\x00\x00\x00\x04\x00'
MPIndivImageNumber type= b'\x04\x00'
MPIndivImageNumber=1
BaseViewpointNumber type= b'\x04\x00'
BaseViewpointNumber=1
ConvergenceAngle type= b'\n\x00'
ConvergenceAngle=136
BaselineLength type= b'\x05\x00'
BaselineLength=144
field?
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00M\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
remaining size 50
new index 7817
** EOI at 206694
new index 206695
** EOI at 214436
new index 214437
** APP2
length= 126
MPF
in parse_identifier_segment
MP_endian b'II*\x00' little
offset 8
count 5
MPIndivImageNumber type= b'\x04\x00'
MPIndivImageNumber=2
BaseViewpointNumber type= b'\x04\x00'
BaseViewpointNumber=1
ConvergenceAngle type= b'\n\x00'
ConvergenceAngle=74
BaselineLength type= b'\x05\x00'
BaselineLength=82
field?
b'\x00\x00\x00\x00\x16\x00\x00\x00\n\x00\x00\x00M\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
remaining size 48
new index 214563
** EOI at 414465
new index 414466
----------------------------------
-----Start JPEG analyse : 391369 bytes
** SOI
** APP0
length= 16
JFIF
in parse_identifier_segment
JFIF analysis
b'\x01\x01\x00\x00\x01\x00\x01\x00\x00'
new index 20
** APP2
length= 76
MPF
in parse_identifier_segment
MP_endian b'II*\x00' little
offset 8
count 2
NumberOfImageTagID 45057 b'\x01\xb0'
numberOfImage type= b'\x04\x00'
count for NumberOfImageTagID 1
nb_of_images = 2
MPEntryTagID 45058 b'\x02\xb0'
MPEntry type b'\x07\x00'
count for MPEntryTagID 32
image 1 :
b'\x03\x00\x96\xfa\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00'
image 1 : attribute=4204134403 size=2 offset=0 dep1=0 dep2=2 image 2 : b'\x02\x003\xfe\x02\x00z\xfa\x02\x00\x00\x00'
image 2 : attribute=4264755202 size=4202299394 offset=2 dep1=0 dep2=0 end 2nd field new index 96
** EOI at 195221
new index 195222
** EOI at 391368
new index 391369
|
Hello Andrew,
The MPVersion field seems to be mandatory in the specification : so it may be THE point that is making a load error in the camera ?
***@***.***
Kind regards
Frank
|
Here's my next attempt then
|
Closing this issue as no feedback has been received. |
#6735 has been merged, adding the MP Format Version. That may or may not resolve this issue, but without further feedback, there is no way to know. |
Hello Andrew
Sorry for not giving news
All 3 attempt files still generate a load error.
Plain jpeg from pillow do not generate errors but are not 3d.
I am testing lots of different options to make it work but for the moment I have not been able to find any solution.
When I look at the differences with 3d images that work, there is one thing I do not understand in these files: the image offset field that is given in the 1st image index of MPF segment does not seem to match the specification that I have found on CIPA website site.
Do you have any idea about how the offset should be based linked to all segments size : jfif, exif (you use something like 28 + im1.size)
Kind regards
Frank
|
To be clear, there's the "Offset of Next IFD", and there's the "Individual image Data Offset". I gather you're asking about the "Individual image Data Offset". From https://web.archive.org/web/20190227081740/http://www.cipa.jp/std/documents/e/DC-007_E.pdf,
Pillow is just writing out each image, getting the position afterwards, and calculating how far that is since the end of the last image. "relative to the address of the MP Endian field" is where 28 comes from. |
Thank you Andrew
I’m doing new tests but only able to proceed during the weekends 😉
I’ll keep you in touch if I succeed
Kind regards
Frank
|
At the moment, you've provided one image that works, presumably directly from the camera. You say that Stereo Photo Maker can generate a valid image. Could you share a valid image generated by Stereo Photo Maker? Having a second point of reference could help, because we could look for common elements between your two working images. |
I will
In fact we can open any photo generated by pillow in spm and then save it as mpo and it works
Kind regards
Frank
|
Hello Andrew,
Yesturday I was able for the first time to generate a MPO file compatible with Fujifilm camera.
I had to make a lot of changes to mimic the file format.
I will continue to work on identifying what changes are not necessary and send you minimal requested changes.
By the way, I need to create MPO Images from two JPEGs (left, right)
I plan to create a class methods like
MPOImageFile.create(left : Image, right : Image) -> MPOImageFile
MPOImageFile.fromJPS(im : Image) -> MPOImageFile
And a method
MPOImageFile.toJPS(self) -> JPEGImageFile
What do you think about these additions? Could it be inserted in PILLOW or should it be outside of PILLOW?
Kind regards
Frank
|
Congratulations on figuring out a solution. Shouldn't As for from PIL import Image, ImageSequence
im = Image.open("63460_pbz98_3D_MPO_70pc.mpo")
for i, frame in enumerate(ImageSequence.Iterator(im)):
im.save(str(i)+".jpg") |
JPS is a special kind of JPEG
Left and right images are stored in just 1 frame, side by side.
The fromJPS method will cut the two sides of the JPS image to create an multi frame Image to be stored as MPO.
The toJPS method stores only one JPEG image and not 2.
This is not a complicated method but can be useful.
I will try left.save(mpo_output_filename, save_all=True, append_images=[right]). Thank you for the proposal.
|
Oh. I did not know this. If you think that is a common operation that users dealing with MPO files would perform, then sure, feel free to create a PR for it. |
Hello Andrew
I'm still digging on the minimal changes to do to be able to generate fujifilm compliant MPOs.
I've found that as soon as I add a JFIF segment to a working MPO.: It makes fujifilm load fail.
Currently it seems to me that jpeg and MPO saves in pillow always include a JFIF segment.
So the best way to remove this segment is to save as MPO and then open the saved file and remove the segment by byte read and byte write operations on the file.
Have you got a better idea to do this?
Kind regards
Frank
|
If I made this change (you won't want to do this exactly, but it works as a demonstration), index 2a24eff39..e7a6562f6 100644
--- a/src/libImaging/JpegEncode.c
+++ b/src/libImaging/JpegEncode.c
@@ -210,12 +210,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
}
context->cinfo.smoothing_factor = context->smooth;
context->cinfo.optimize_coding = (boolean)context->optimize;
- if (context->xdpi > 0 && context->ydpi > 0) {
- context->cinfo.write_JFIF_header = TRUE;
- context->cinfo.density_unit = 1; /* dots per inch */
- context->cinfo.X_density = context->xdpi;
- context->cinfo.Y_density = context->ydpi;
- }
+ context->cinfo.write_JFIF_header = FALSE;
switch (context->streamtype) {
case 1:
/* tables only -- not yet implemented */ then when I run from PIL import Image
im = Image.open("in.mpo")
im.save("out.mpo", save_all=True)
|
Hello Andrew,
The out.mpo file you sent me is buggy
I've analysed the first bytes and it shows immediately an error
In the list below
- x = out.mpo
- y = well formed MPO file example
Apart from the fact that your file has no EXIF segment, the MPF segment is KO (APP2).
The error starts at line 010 where we have spaces (HEX=20) instead of having Endianess information (II*...) that finally comes after the spaces at line 028.
JFIF segment has a length of 18 bytes so it seems that your program is still reserving the place for JFIF segment (with spaces) which may be overridden afterwards.
So I think that you will find in the code a place where you write 18 spaces and that this part of code should be commented.
Kind regards
Frank
line 000 x= FF y= FF
line 001 x= D8 y= D8 SOI SOI
line 002 x= FF y= FF
->line 003 x= E2 y= E1 APP2 APP1
->line 004 x= 00 y= 1C
->line 005 x= 58 y= C6 X
->line 006 x= 4D y= 45 M E
->line 007 x= 50 y= 78 P x
->line 008 x= 46 y= 69 F i
->line 009 x= 00 y= 66 f
->line 010 x= 20 y= 00
->line 011 x= 20 y= 00
->line 012 x= 20 y= 49 I
->line 013 x= 20 y= 49 I
->line 014 x= 20 y= 2A *
->line 015 x= 20 y= 00
->line 016 x= 20 y= 08
->line 017 x= 20 y= 00
->line 018 x= 20 y= 00
->line 019 x= 20 y= 00
->line 020 x= 20 y= 0C
->line 021 x= 20 y= 00
->line 022 x= 20 y= 0F
->line 023 x= 20 y= 01
->line 024 x= 20 y= 02
->line 025 x= 20 y= 00
->line 026 x= 20 y= 09
->line 027 x= 20 y= 00
->line 028 x= 49 y= 00 I
->line 029 x= 49 y= 00 I
->line 030 x= 2A y= 9E *
line 031 x= 00 y= 00
->line 032 x= 08 y= 00
line 033 x= 00 y= 00
->line 034 x= 00 y= 10
->line 035 x= 00 y= 01
->line 036 x= 03 y= 02
|
I'm not convinced that our MPO files shouldn't contain a JFIF header just because your camera doesn't accept that? If you can find something in the specification that says that we shouldn't have it, then sure. This might be another form of #812. http://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_JPEG_files
|
Hi Andrew
Here is the specification I've been working with to solve my issues.
https://www.cipa.jp/std/documents/download_e.html?CIPA_DC-007-2021_E
On page 11, you can see what segments should be in an MPO file.
Kind regards
Frank
|
Looking at the specification, I've come up with a different interpretation - perhaps it is not that we shouldn't have a JFIF header, but that the APP1 and APP2 markers should come first? I've created PR #6839 for this. See what you think. |
Hello Andrew
It seems to me that it would make sense to have APP0 then APP1 then APP2 etc. (other APPn exist for some file types)
This seems the logic of this kind of specification. Another example is that tags keys should also be ordered
Kind regards
Frank
|
Looking through Pillow's test images, I found 17 images that have the markers not in APPn order - rgb.jpg, no-dpi-in-exif.jpg, xmp_test.jpg, exif-dpi-zerodivision.jpg, flower2.jpg, pil_sample_cmyk.jpg, exif-ifd-offset.jpg, exif-200dpcm.jpg, jpeg_ff00_header.jpg, app13-multiple.jpg, truncated_app14.jpg, photoshop-200dpi-broken.jpg, pil_sample_rgb.jpg, photoshop-200dpi.jpg, invalid-exif.jpg and iptc_roundUp.jpg, iptc.jpg. |
I noticed that in the specification, it says
So I conclude that yes, this should be EXIF JPEG data, not JFIF. I don't know if that excludes the possibility of having a APP0 JFIF marker after APP2. As I've said, many of our test images don't feel the need to have the markers be in order, and I can't find much on Google talking about it. I have two branches - one that allows APP1, APP2 and then APP0, and one that prevents APP0 completely. Would you mind testing both of them with your camera? |
Hello Andrew
Could you explain to me how I can use the forks?
Usually I just do pip install....
Tia
Frank
|
Ah, you're a Windows user, so building from source is not so simple. Fortunately, GitHub Actions generates wheels for us. So here are the wheels you will need - Pillow mpo wheels.zip. I don't know if you're 32 or 64-bit, so I've included both. You should be able to just |
Hello Andrew
I wish you a very happy new year.
Concerning the fix you sent to me
It seems that either the wheel did not work or the fix did not work well
I ended up with this simple test program and its output shows that nothing have changed concerning JFIF :
def test_jfif():
im = Image.open('input/left.jpg')
im.save('left_wo_jfif.jpg')
fp = open('left_wo_jfif.jpg', 'rb')
bytes_20 = fp.read(20)
for i in range(0,10):
print(bytes_20[i*2:i*2+2])
output :
b'\xff\xd8'
b'\xff\xe0'
b'\x00\x10'
b'JF'
b'IF'
b'\x00\x01'
b'\x01\x00'
b'\x00\x01'
b'\x00\x01'
b'\x00\x00'
Does it do the same for you (any jpeg file will do the same) ?
Is there a chance that the c library was not updated?
Kind regards
Frank
|
If you're having trouble installing the wheels, then instead, here is your image saved with each of those branches - jfif_attempts.zip |
Hello Andrew
I think I needed to add - - upgrade to the pip command
That may explain why I do not see any changes
I will try this evening
Kind regards
Frank
|
Hello Andrew,
To make a valid MPO file for Fujifilm there is a bit more to do :
1) An APP1 EXIF segment is compulsory (but can be rather minimal e.g. Orientation, Width and Height of the image)
2) JFIF Segment must not be present
3) APP2 MPF needs to include some fields including one of type SRational
-> so I created this missing type in TiffImagePlugin attached
Then if you rename MpoImagePluginFujifilm in MPOImagePlugin and use it with your v9.4 that does not generate JFIF at all. This should work alright :-)
Unfortunately I was not able to test because even the pip --upgrade did not work (the PIL .py files were updated but not the C library). So I did the last modifications of the MpoImagePlugin without being able to test it.
Kind regards
Frank
|
Fyi, no attachment came through. |
Hi Andrew,
I think I needed to zip them so they are not blocked by antispam/antivirus
Sorry for that
Kind regards
Frank
|
Still nothing. It might not be possible for attachments to work through e-mail replies. If you would like to include it here, you could try doing so through the web interface? |
PIL.zip |
Hello Andrew, |
I presume that these images did not work with your camera. |
Looking at your attachments, I'm not seeing any changes in your TiffImagePlugin, but for MpoImagePlugin, you have
And you've also said that you exclude JFIF. The changes are very specific (Pillow doesn't even know what the Convergence Angle of the two images is), and I'm not seeing any evidence that these changes are required by a specification. I congratulate you on figuring out how to get the images working in your camera, but I'm reluctant to say that all MPO images generated by Pillow must adhere to these standards, just for the sake of being read by Fujifilm cameras. If you would like Pillow users to be able to control the MP Types, or to manually set Base Viewpoint Number, ConvergenceAngle and BaselineLength, sure, those seems like reasonable feature requests. However, changing the defaults and adding specific values to work in a particular situation doesn't seem justified. Is this not something that you could just add as another plugin to your code, using |
Closing this issue as no feedback has been received. |
What did you do?
When loading and then saving an .mpo image from fujifilm camera, the saved image breaks when viewed with a Fujifilm w3 camera (with autostereoscopic screen). There is a loading error displayed in the camera. 3D mpo loading in Pillow is ok it is possible to get left and right images but writing does not seem to be fully compatible.
My script to do this :
What did you expect to happen?
saved image to open properly in Fujifilm camera viewer
What actually happened?
a loading error
What are your OS, Python and Pillow versions?
Image file is provided with a link as Github does not support upload of .mpo files : http://www.christian-roux.ch/mpo/63460_pbz98_3D_MPO_70pc.mpo
It is just an example as I have not found any .mpo working after a
im.save(mpo_output_filename, save_all=True)
. I have tried many. NB : the program Stereo Photo Maker is able to generate valid .mpo for Fujifilm cameras.The text was updated successfully, but these errors were encountered: