How to add File Format to FileIO.jl and Image related formats to ImageIO.jl

Ashwani Rathee
4 min readJan 1, 2023

--

In this post, I document how to add new file formats to FileIO.jl and ImageIO.jl . Let’s say you have written JpegTurbo.jl which provides load and save support for jpeg, jpg files.

JpegTurbo.jl

Support for decoding of a file/iostream and encoding of a file/iostream are located in decode.jl and encode.jl of src/ folder of the project.

using JpegTurbo
img = rand(64, 64)
# jpeg_encode provides encoding support
bytes = jpeg_encode(img) # Vector{UInt8}
# jpeg_decode provides decoding support
img_saveload = jpeg_decode(bytes) # size: 64x64

To start to work on this, we need to create fileio.jl which provides method that FileIO.jl to use jpeg_decodeand jpeg_encode provided by JpegTurbo.jl .

We create fileio.jl and add it to JpegTurbo.jl, write the below written code which i’ll explain in a bit and write the tests. There will be slight difference depending on your specific file format. Like File{format"JPEG”}and jpeg_decode and jpeg_encode will be changed mostly.

using FileIO

# These are private load, save function that will be conditionally
# called by fileio as needed

function fileio_load(f::File{format"JPEG"}; kwargs...)
open(f.filename, "r") do io
jpeg_decode(io; kwargs...)
end
end

fileio_load(io::Stream{format"JPEG"}; kwargs...) = jpeg_decode(read(io); kwargs...)

# both load and save take File and Stream object
# more info on them here:
# https://juliaio.github.io/FileIO.jl/stable/reference/#FileIO.File
function fileio_save(f::File{format"JPEG"}, img::AbstractArray; kwargs...)
open(f.filename, "w") do io
jpeg_encode(io, img; kwargs...)
end
end

function fileio_save(io::Stream{format"JPEG"}, img::AbstractArray; kwargs...)
jpeg_encode(io.io, img; kwargs...)
end

# inside these functions like fileio_load and fileio_save we have methods
# from JpegTurbo that do the heavy lifting.

More details in this PR:

FileIO support

FileIO aims to provide a common framework for detecting file formats and dispatching to appropriate readers/writers. The two core functions in this package are called load and save, and offer high-level support for formatted files (in contrast with julia's low-level read and write).

FileIO provides two method for every file format that’s registered: load and save, neat isn’t it~

When FileIO detects that a file or stream should be handled by a particular package, it will try to call private methods in that package for processing the request. For example, suppose you have created a package called JpegTurbo to handle files of a particular format; then load("somefile.jpeg") for a suitable file will cause FileIO to:

  • attempt to load your package JpegTurbo using Base.require(id::PkgId), where a PkgId combines the name and UUID that you supplied via add_format
  • call JpegTurbo.load(file) where file is File.

Important point is that JpegTurbo.load does not extend FileIO.load: it is a private function defined in module JpegTurbo. This is important for ensuring that single formats can be supported by multiple packages by module-qualification.

More details can be found here:

So we have written fileio.jl in JpegTurbo.jl, then we write the tests like here:

We write the test set to test the functions in src/fileio.jl and include it in runtests.jl

@testset "FileIO" begin
img = testimage("cameraman")
ref = jpeg_decode(jpeg_encode(img))
tmpfile = File{format"JPEG"}(joinpath(tmpdir, "tmp.jpg"))
JpegTurbo.fileio_save(tmpfile, img)
data = JpegTurbo.fileio_load(tmpfile)

# checks if original file and file encoded by JpegTurbo are same or not.
@test data == ref
end

Support in FileIO.jl

Adding the file format in FileIO.jl is quite simple:

  • We need to add details about the package in registry of FileIO.jl

To do that we add below written code to src/registry.jl in FileIO.jl repo :

const idJpegTurbo = :JpegTurbo => UUID("b835a17e-a41a-41e7-81f0-2f016b05efe0")

add_format(
format"JPEG", # DataFormat type, created as format"IDENTIFIER"
UInt8[0xff,0xd8,0xff], # Magic Bytes, Bytes that uniquely identify format
[".jpeg", ".jpg", ".JPG"], # types of file decode and encodable
[idJpegTurbo], # first choice
[idQuartzImageIO, OSX], # choice after JpegTurbo incase JpegTurbo fails in OSX
[idImageMagick] # last choice
) # 0xe1

We want to sunset ImageMagick.jl in the long run hence it being the last choice.

More details can be found here:

And for ImageIO.jl, we do something similar for which more details can be found here:

It has similar load, save functions for ImageIO and tests after that. Hope you got to know about it and learn more on the topic.

--

--

Ashwani Rathee
Ashwani Rathee

Written by Ashwani Rathee

From house of sangrinmid. I am a Julian. I like movies, music, code and everything stitch.

No responses yet

Write a response