Recientemente han llamado mi atención sobre el formato de imagen MPO (Multi-Picture Format), un formato que se usa dentro del campo de la fotografía en 3D, y concretamente es el utilizado por la cámara FinePix REAL 3D. Básicamente un fichero MPO esta compuesto por una imagen principal, y una serie de imágenes secundarias, guardadas en formato JPEG. La imagen principal está colocada justo al comienzo del fichero de tal forma que podría ser vista por cualquier visor de imágenes jpeg, mientras que para ver el resto de imágenes necesitamos de algún software especifico que soporte este formato.
Como decía, mi interés por este formato viene dado por una pregunta de otro programador que quería saber como extraer de un fichero MPO cada una las diferentes imágenes jpeg que lo componen. Entonces recordé el código para recuperar imágenes jpeg que coloque por aquí, si bien ese programa estaba pensado para recuperar fotos borradas, también sirve para extraer una imagen jpeg de cualquier archivo en el que este incrustada (pdf, thumb.db, etc ...) y por lo tanto tampoco tiene ningún problema en extraer las imágenes jpeg de un fichero MPO.
El problema que surgió entonces fue la velocidad, el proceso era muy lento, sobre todo con imágenes grandes. Pero la solución era sencilla, cargar el fichero MPO en memoria antes de comenzar a explorarlo, esto incrementa de forma radical la velocidad de todo proceso. En el código original no se cargaba el fichero de origen en memoria porque estaba pensado para examinar discos completos, y no tiene mucho sentido cargar un fichero de varios gigas en memoria, pero cuando tratamos con ficheros de pocos megas, como es el caso de los MPO no hay problema.
El resultado de todo lo anterior es este pequeño programa:
program mpo2jpg; uses Windows, SysUtils, Classes; procedure CopyJpegFrom(Src, Dst: TStream); var Buffer: PByteArray; Size: Integer; begin GetMem(Buffer,$10000); try while TRUE do begin FillChar(Buffer^,4,#0); Src.Read(Buffer^,4); Size:= ((Buffer[2] shl 8) + Buffer[3]) - 2; case Buffer[1] of $01,$D0..$D8: begin Dst.WriteBuffer(Buffer^,2); Src.Seek(-2,soFromCurrent); end; $D9: begin Dst.WriteBuffer(Buffer^,2); Exit; end; $DA: begin Dst.WriteBuffer(Buffer^,2); Src.Seek(-2,soFromCurrent); while TRUE do begin Src.ReadBuffer(Buffer^,1); if Buffer[0] = $FF then begin Src.ReadBuffer(Buffer^,1); if (Buffer[0] <> 0) and not (Buffer[0] in [$D0..$D7]) then begin Src.Seek(-2,soFromCurrent); break; end else begin Buffer[1]:= Buffer[0]; Buffer[0]:= $FF; Dst.WriteBuffer(Buffer^,2); end; end else Dst.WriteBuffer(Buffer^,1); end; end else begin Dst.WriteBuffer(Buffer^,4); Src.ReadBuffer(Buffer^,Size); Dst.WriteBuffer(Buffer^,Size); end; end; end; finally FreeMem(Buffer); end; end; procedure SaveJpegToFile(Src: TStream; Filename: String); var Stream: TMemoryStream; begin Stream:= TMemoryStream.Create; try CopyJpegFrom(Src, Stream); Stream.SaveToFile(Filename); finally Stream.Free; end; end; procedure ScanFile(Stream: TStream; FormatStr: String; Index: Integer; Full: Boolean); overload; var B: Byte; Leidos: Integer; Posicion: int64; Str: String; begin // Leemos byte a byte hasta encontrar la marca de inicio Leidos:= Stream.Read(B,1); while Leidos = 1 do if B = $FF then begin Leidos:= Stream.Read(B,1); if Leidos = 1 then if B = $D8 then begin Leidos:= Stream.Read(B,1); if Leidos = 1 then if B = $FF then begin // Encontramos la marca de inicio Posicion:= Stream.Position; Stream.Seek(-3,soFromCurrent); Str:= Format(FormatStr,[index]); try // Intentamos extraer la imagen SaveJpegToFile(Stream,Str); inc(index); except // Si se produce un error volvemos atras y continuamos buscando On E: Exception do begin Stream.Position:= Posicion; end; end; // Si la busqueda es "completa", extraemos tambien los thumbnails if Full then Stream.Position:= Posicion; end; end; end else Leidos:= Stream.Read(B,1); end; var Src: TMemoryStream; begin if ParamCount = 0 then Exit; try Src:= TMemoryStream.Create; try // Cargamos el archivo en memoria Src.LoadFromFile(ParamStr(1)); // Exploramos el archivo cargado en la memoria ScanFile(Src,ChangeFileExt(ParamStr(1),'_%d.jpg'),1,FALSE); finally Src.Free; end; except On E: Exception do begin // Si tenemos algun error lo mostramos MessageBox(0,PChar(E.Message),'Error',MB_ICONERROR or MB_SETFOREGROUND or MB_TASKMODAL or MB_OK); end; end; end.
Su uso es sencillo, solo hay que pesarle como parámetro el fichero MPO (o simplemente arrastrar el fichero y soltarlo sobre el .exe) y automáticamente el programa extraerá la imágenes en el mismo directorio del fichero original (hay que comprobar primero que ese directorio no sea de "solo lectura").
Por ejemplo:
mpo2jpg ejemplo.mpo
El programa completo, con sus fuentes, se puede bajar de aquí. También se puede utilizar para extraer imágenes de ficheros pdf, thumb.db, etc ... siempre que tengan un tamaño razonable.
Para saber más sobre el formato MPO:
http://www.cipa.jp/english/hyoujunka/kikaku/pdf/DC-007_E.pdf
Comentarios
Muchas gracias por compartir
Muchas gracias por compartir el código fuente. Además, es un programa muy útil.
Hola: muchas gracias por este
Hola, muchas gracias por este programa. La cámara fuji3d está configurada de tal manera que no puedes hacer las fotos simultáneas idénticas. De esta manera no podía sacar las fotos para montarlas en un estereoscopio, o las tenía que manipular.
Así que me viene fenomenal. Mil gracias.
María.
hola oye sabes como puedo
Sabes como puedo recuperar la otra imagen, solo aparece la principal y quisiera tener las 2 (izquierdo y derecho)
gracias
Pues deberían de aparecer las
Pues deberían de aparecer las dos, si no aparecen es que en el fichero solo hay una imagen y no dos.
Saludos
En la página de fuji se puede
En la página de fuji se puede descargar el programa myfinepix studio en donde se pueden crear archivos mpo y separar los existentes, el problema es que hay que hacerlo de uno por uno y es tedioso y muy lento. saludos!!!