23. Serialization#
// Halide tutorial lesson 23: Serialization
// This lesson describes how to serialize pipelines into a binary format
// which can be saved on disk, and later deserialized and loaded for
// evaluation.
// Note that you'll need to be using a build of Halide that was configured
// using the WITH_SERIALIZATION=ON macro defined in order for this tutorial
// to work.
// Disclaimer: Serialization is experimental in Halide 17 and is subject to
// change; we recommend that you avoid relying on it for production work at this time.
// On linux, you can compile this tutorial and run it like so:
// g++ lesson_23*.cpp -g -I <path/to/include> -I <path/to/tools> -L <path/to/lib> -lHalide $(pkg-config --cflags --libs libpng libjpeg) -lpthread -ldl -o lesson_23 -std=c++17
// LD_LIBRARY_PATH=<path/to/lib> ./lesson_23
// On macOS:
// g++ lesson_23*.cpp -g -I <path/to/include> -I <path/to/tools> -L <path/to/lib> -lHalide $(pkg-config --cflags --libs libpng libjpeg) -o lesson_23 -std=c++17
// DYLD_LIBRARY_PATH=<path/to/lib> ./lesson_23
#include "Halide.h"
#include <algorithm>
#include <cstdio>
using namespace Halide;
// Support code for loading pngs.
#include "halide_image_io.h"
int main() {
// First we'll declare some Vars to use below.
Var x("x"), y("y"), c("c");
// Let's start with the same separable blur pipeline that we used in Tutorial 7,
// with the clamped boundary condition
{
// Let's create an ImageParam for an 8-bit RGB image that we'll use for input.
ImageParam input(UInt(8), 3, "input");
// Wrap the input in a Func that prevents reading out of bounds:
Func clamped("clamped");
Expr clamped_x = clamp(x, 0, input.width() - 1);
Expr clamped_y = clamp(y, 0, input.height() - 1);
clamped(x, y, c) = input(clamped_x, clamped_y, c);
// Upgrade it to 16-bit, so we can do math without it overflowing.
Func input_16("input_16");
input_16(x, y, c) = cast<uint16_t>(clamped(x, y, c));
// Blur it horizontally:
Func blur_x("blur_x");
blur_x(x, y, c) = (input_16(x - 1, y, c) +
2 * input_16(x, y, c) +
input_16(x + 1, y, c)) / 4;
// Blur it vertically:
Func blur_y("blur_y");
blur_y(x, y, c) = (blur_x(x, y - 1, c) +
2 * blur_x(x, y, c) +
blur_x(x, y + 1, c)) / 4;
// Convert back to 8-bit.
Func output("output");
output(x, y, c) = cast<uint8_t>(blur_y(x, y, c));
// Now lets serialize the pipeline to disk (must use the .hlpipe file extension)
Pipeline blur_pipeline(output);
std::map<std::string, Parameter> params;
serialize_pipeline(blur_pipeline, "blur.hlpipe", params);
// The call to serialize_pipeline populates the params map with any input or output parameters
// that were found ... object's we'll need to attach to buffers if we wish to execute the pipeline
for(const auto &[name, _param]: params) {
std::cout << "Found Param: " << name << "\n";
}
}
// new scope ... everything above is now destroyed! Now lets reconstruct the entire pipeline
// from scratch by deserializing it from a file
{
// Lets load a color 8-bit input and connect it to an ImageParam
Buffer<uint8_t> rgb_image = Halide::Tools::load_image("images/rgb.png");
ImageParam input(UInt(8), 3, "input");
input.set(rgb_image);
// Now lets populate the params map so we can override the input
std::map<std::string, Parameter> params;
params.insert({"input", input.parameter()});
// Lets construct a new pipeline from scratch by deserializing the file we wrote to disk
Pipeline blur_pipeline = deserialize_pipeline("blur.hlpipe", params);
// Now realize the pipeline and blur out input image
Buffer<uint8_t> result = blur_pipeline.realize({rgb_image.width(), rgb_image.height(), 3});
// Now lets save the result ... we should have another blurry parrot!
Halide::Tools::save_image(result, "another_blurry_parrot.png");
}
// new scope ... everything above is now destroyed!
{
// Lets do the same thing again ... construct a new pipeline from scratch by deserializing the file we wrote to disk
// First we can deserialize the external parameters (useful in the event we want to remap them
// and replace the definitions with our own user parameter definitions)
std::map<std::string, Parameter> params = deserialize_parameters("blur.hlpipe");
// Now deserialize the pipeline from file
Pipeline blur_pipeline = deserialize_pipeline("blur.hlpipe", params);
// Now, lets serialize it to an in memory buffer ... rather than writing it to disk
std::vector<uint8_t> data;
serialize_pipeline(blur_pipeline, data, params);
// Now lets deserialize it from memory
Pipeline deserialized = deserialize_pipeline(data, params);
}
printf("Success!\n");
return 0;
}
#!/usr/bin/python3
# Halide tutorial lesson 23: Serialization
# This lesson describes how to serialize pipelines into a binary format
# which can be saved on disk, and later deserialized and loaded for
# evaluation.
# Note that you'll need to be using a build of Halide that was configured
# using the WITH_SERIALIZATION=ON macro defined in order for this tutorial
# to work.
# Disclaimer: Serialization is experimental in Halide 17 and is subject to
# change; we recommend that you avoid relying on it for production work at
# this time.
import os.path
import numpy as np
import halide as hl
import halide.imageio
def main():
# First we'll declare some Vars to use below.
x, y, c = hl.Var("x"), hl.Var("y"), hl.Var("c")
# Let's start with the same separable blur pipeline that we used in
# Tutorial 7, with the clamped boundary condition
if True:
# Let's create an ImageParam for an 8-bit RGB image that we'll use
# for input.
input = hl.ImageParam(hl.UInt(8), 3, "input")
# Wrap the input in a Func that prevents reading out of bounds:
clamped = hl.Func("clamped")
clamped_x = hl.clamp(x, 0, input.width() - 1)
clamped_y = hl.clamp(y, 0, input.height() - 1)
clamped[x, y, c] = input[clamped_x, clamped_y, c]
# Upgrade it to 16-bit, so we can do math without it overflowing.
input_16 = hl.Func("input_16")
input_16[x, y, c] = hl.u16(clamped[x, y, c])
# Blur it horizontally:
blur_x = hl.Func("blur_x")
blur_x[x, y, c] = (
input_16[x - 1, y, c] + 2 * input_16[x, y, c] + input_16[x + 1, y, c]
) // 4
# Blur it vertically:
blur_y = hl.Func("blur_y")
blur_y[x, y, c] = (
blur_x[x, y - 1, c] + 2 * blur_x[x, y, c] + blur_x[x, y + 1, c]
) // 4
# Convert back to 8-bit.
output = hl.Func("output")
output[x, y, c] = hl.u8(blur_y[x, y, c])
# Now lets serialize the pipeline to disk (must use the .hlpipe file
# extension)
blur_pipeline = hl.Pipeline(output)
params = hl.serialize_pipeline(blur_pipeline, "blur.hlpipe", get_params=True)
# The call to serialize_pipeline populates the params map with any
# input or output parameters that were found ... objects we'll need
# to attach to buffers if we wish to execute the pipeline
for name in params:
print(f"Found Param: {name}")
# new scope ... everything above is now unreachable! Now lets
# reconstruct the entire pipeline from scratch by deserializing it from
# a file
if True:
# Lets load a color 8-bit input and connect it to an ImageParam
image_path = os.path.join(
os.path.dirname(__file__), "../../tutorial/images/rgb.png"
)
rgb_image = hl.Buffer(halide.imageio.imread(image_path))
input = hl.ImageParam(hl.UInt(8), 3, "input")
input.set(rgb_image)
# Now lets populate the params map so we can override the input
params = {"input": input.parameter()}
# Lets construct a new pipeline from scratch by deserializing the
# file we wrote to disk
blur_pipeline = hl.deserialize_pipeline("blur.hlpipe", params)
# Now realize the pipeline and blur out input image
result = blur_pipeline.realize([rgb_image.width(), rgb_image.height(), 3])
# Now lets save the result ... we should have another blurry parrot!
# python3-imageio versions <2.5 expect a numpy array
halide.imageio.imwrite("another_blurry_parrot.png", np.asanyarray(result))
# new scope ... everything above is now unreachable!
if True:
# Lets do the same thing again ... construct a new pipeline from
# scratch by deserializing the file we wrote to disk
# First we can deserialize the external parameters (useful in the
# event we want to remap them and replace the definitions with our
# own user parameter definitions)
params = hl.deserialize_parameters("blur.hlpipe")
# Now deserialize the pipeline from file
blur_pipeline = hl.deserialize_pipeline("blur.hlpipe", params)
# Now, lets serialize it to an in memory buffer ... rather than
# writing it to disk
data, params = hl.serialize_pipeline(blur_pipeline, get_params=True)
# Now lets deserialize it from memory
hl.deserialize_pipeline(data, params)
print("Success!")
return 0
if __name__ == "__main__":
main()