# Julia: Calling C Module

In one specific task, I need to extract the DCT coefficients from a JPEG image and export them into Julia. Since there are a number of existing C libraries that are capable of doing this, it is more convenient to call them directly with Julia.

## The library

In order to extract DCT coefficients from JPEG images, we need to call libjpeg APIs. Fortunately, there has been existing wrappers on GitHub that encapsulates the complicated libjpeg function calls. To download the package, we can clone the repository to the Julia source code’s directory.

git clone https://github.com/klauscc/jpeg_toolbox_python.git


Since we are not building the Python bindings, we can comment out the last line in CMakeLists.txt:

#add_subdirectory(python)


And then we build the dynamic library inside the build folder.

$mkdir build$ cd build
$cmake ..$ make


If everything is successful, we should be able to see libextract_dct.so in build directory.

\$ ls
CMakeCache.txt	cmake_install.cmake  Makefile


## Exposing C structures and functions to Julia

There are two functions provided by the C package, namely read_jpeg and freeJpegObj. We need to export these two functions to Julia so that we can use its results. Firstly, we define a Julia structure that is identical to jpegobj in C.

struct CJpegObj
image_width::Int32
image_height::Int32
image_components::Int32
image_color_space::Int32
quant_nums::Int32
coef_array_shape::NTuple{4, NTuple{2, Int32}}
quant_tables::Ptr{Float64}
coef_arrays::Ptr{Ptr{Float64}}
end


Then, we load the functions from the dynamic library into Julia.

using Libdl

jpegTool = Libdl.dlopen("jpeg_toolbox_python/build/libextract_dct.so")
free_jpeg_obj = Libdl.dlsym(jpegTool, :freeJpegObj)


We can create a Julia type to store the data from C library and use a function to copy data from C to Julia in order to properly manage resource.

mutable struct JpegObj
image_width::Int32
image_height::Int32
image_components::Int32
image_color_space::Int32
quant_nums::Int32
coef_array_shape::Array{Int32, 2}
quant_tables::Array{Array{Float64, 2}, 1}
coef_arrays::Array{Array{Float64, 2}, 1}
end

using Base.Filesystem

# this function can create potential memory leak when there is an exception!
@assert isfile(imgname)

# get object
cJpegObj = ccall(read_jpeg, CJpegObj, (Cstring,), imgname)

# copy the data from C to julia
jpegObj = JpegObj(
cJpegObj.image_width,
cJpegObj.image_height,
cJpegObj.image_components,
cJpegObj.image_color_space,
cJpegObj.quant_nums,
Array{Int32, 2}(undef, 3, 2),
Array{Array{Float64, 2}, 1}(undef, 0),
Array{Array{Float64, 2}, 1}(undef, 3)
)

println(cJpegObj)

# copy dct array shapes
for i in 1:3, j in 1:2
jpegObj.coef_array_shape[i, j] = cJpegObj.coef_array_shape[i][j]
end

# copy quantization tables
# always assume dct size is 8
@assert jpegObj.quant_nums >= 1
jpegObj.quant_tables = fill(Array{Float64, 2}(undef, 8, 8), jpegObj.quant_nums)
for i in 1:jpegObj.quant_nums, x in 1:8, y in 1:8
offset = (i - 1) * 64 + (x - 1) * 8 + y
end

# copy dct arrays
for i in 1:3
rows = jpegObj.coef_array_shape[i, 1]
cols = jpegObj.coef_array_shape[i, 2]
jpegObj.coef_arrays[i] = Array{Float64, 2}(undef, rows, cols)