Two functions must be provided to integrate an external JBIG executable: encode(matrix)
for encoding process and decode(payload)
for decoding process.
The functions call jbig executeable and then do its respective task.
Here we provides an example based on JBIG-KIT.
First, create a folder called third_party
, then download and extract JBIG-KIT to this folder.
Compile the JBIG-KIT source code to produce executables for both encoding and decoding process.
Here we use pbmtojbg85
for the encoding process and jbgtopbm85
for the decoding proceses.
The next step is to create a new file jbigkit.py
that contains both encode and decode functions.
Both functions should specify all necessary flags.
The file shall be located in gvc/codec
folder.
The content should looks like this:
import os
import logging as log
import subprocess as sp
import tempfile as tmp
from PIL import Image
import numpy as np
from .jbig import get_shape
from .. import settings
from .. import utils
# Allow super large image decompression. DO NOT REMOVE!
Image.MAX_IMAGE_PIXELS = np.inf
JBIGKIT_PATH = os.path.join(settings.THIRD_PARTY_DIR, 'jbigkit-2.1', 'pbmtools')
ENCODER_FPATH = os.path.join(JBIGKIT_PATH, "pbmtojbg85") #? Specify path to encoder
DECODER_FPATH = os.path.join(JBIGKIT_PATH, "jbgtopbm85") #? Specify path to decoder
utils.check_executable(ENCODER_FPATH) #? Check if executable exists
utils.check_executable(DECODER_FPATH) #? Check if executable exists
JBG_LRLTWO = 0x40
JBG_VLENGTH = 0x20
JBG_TPBON = 0x08
def encode(
matrix:np.ndarray
) -> bytes:
"""
Encode a binary matrix as JBIG1 bytestream.
Parameters
----------
matrix : ndarray (2d)
binary_matrix: The binary matrix to encode. Must contain integers equal to 0 or 1
Returns
-------
jbig1_binary: binary
The JBIG1 bytestream.
"""
# Initialize temporary directory
with tmp.TemporaryDirectory(prefix='PBM2JBG_') as dpath:
pbm_fpath = os.path.join(dpath, 'tmp.pbm')
jbg_fpath = os.path.join(dpath, 'tmp.jbg')
# Store matrix as PBM file
pbm_im = Image.fromarray(matrix.astype(bool))
pbm_im.save(pbm_fpath)
flags = JBG_TPBON
l0 = (1<<32)-1
# Encoder parameters
args = [
"-p", str(flags),
"-s", str(l0)
]
proc = sp.run([ENCODER_FPATH, *args, pbm_fpath, jbg_fpath], stdout=sp.PIPE, stderr=sp.PIPE)
if proc.returncode != 0:
log.error(proc.stderr)
raise RuntimeError(proc.stderr.decode("UTF-8"))
# Load JBIG payload
with open(jbg_fpath, 'rb') as jbig_f:
jbig_payload = jbig_f.read()
return jbig_payload
def decode(
jbig_payload:bytes
) -> np.ndarray:
"""
Decode a JBIG1 bytestream as binary matrix.
Parameters
----------
jbig_payloadtream: bytes
The JBIG1 bytestream.
Returns
-------
bin_matrix: np.ndarray
The binary matrix
"""
# Initialize temporary directory
with tmp.TemporaryDirectory(prefix='JBG2PBM_') as dpath:
jbg_fpath = os.path.join(dpath, 'tmp.jbg')
pbm_fpath = os.path.join(dpath, 'tmp.pbm')
# Store JBIG payload
with open(jbg_fpath, 'wb') as jbig1_f:
jbig1_f.write(jbig_payload)
__, ncols = get_shape(jbig_payload[:12])
# Decoder parameters
args = [
"-x", str(ncols),
"-B", str(2**30),
]
proc = sp.run([DECODER_FPATH, *args, jbg_fpath, pbm_fpath], stdout=sp.PIPE, stderr=sp.PIPE)
if proc.returncode != 0:
log.error(proc.stderr)
raise RuntimeError(proc.stderr.decode("UTF-8"))
# Load matrix (pbm image)
pbm_im = Image.open(pbm_fpath)
mat = np.asarray(pbm_im)
return mat
Internally, GVC does not extract the header or the parameters of the payload, thus JBIG parameters are independent from GVC.
We then register the encode and decode functions in __init__.py
file located in gvc/codec
:
from . import jbigkit #? Import jbigkit
#? If a new codec is added, please update data_structure.consts too
MAT_CODECS = {
CodecID.JBIG1 : { #? Add new dict entries for additional codec
"name": "jbig",
"encoder": jbigkit.encode, #? Add encode function
"decoder": jbigkit.decode, #? Add decode function
}
}
Now JBIG is ready to use.