diff --git a/README.rst b/README.rst
index f81d6e2e7867..8d9145f58d64 100644
--- a/README.rst
+++ b/README.rst
@@ -71,12 +71,6 @@ for improved debugging: [optional]
         information, and libunwind is not needed if both libdw and the glibc
         backtrace() function are available.
 
-for the Raspberry Pi IPA: [optional]
-        libboost-dev
-
-        Support for Raspberry Pi can be disabled through the meson
-         'pipelines' option to avoid this dependency.
-
 for device hotplug enumeration: [optional]
         libudev-dev
 
diff --git a/src/ipa/raspberrypi/controller/algorithm.cpp b/src/ipa/raspberrypi/controller/algorithm.cpp
index 43ad0a2be222..4fd36fc1be5b 100644
--- a/src/ipa/raspberrypi/controller/algorithm.cpp
+++ b/src/ipa/raspberrypi/controller/algorithm.cpp
@@ -9,7 +9,7 @@
 
 using namespace RPiController;
 
-void Algorithm::Read([[maybe_unused]] boost::property_tree::ptree const &params)
+void Algorithm::Read([[maybe_unused]] const libcamera::YamlObject &params)
 {
 }
 
diff --git a/src/ipa/raspberrypi/controller/algorithm.hpp b/src/ipa/raspberrypi/controller/algorithm.hpp
index 5123c87bab34..87bfca8d2045 100644
--- a/src/ipa/raspberrypi/controller/algorithm.hpp
+++ b/src/ipa/raspberrypi/controller/algorithm.hpp
@@ -13,10 +13,10 @@
 #include <memory>
 #include <map>
 
+#include "libcamera/internal/yaml_parser.h"
+
 #include "controller.hpp"
 
-#include <boost/property_tree/ptree.hpp>
-
 namespace RPiController {
 
 // This defines the basic interface for all control algorithms.
@@ -33,7 +33,7 @@ public:
 	virtual bool IsPaused() const { return paused_; }
 	virtual void Pause() { paused_ = true; }
 	virtual void Resume() { paused_ = false; }
-	virtual void Read(boost::property_tree::ptree const &params);
+	virtual void Read(const libcamera::YamlObject &params);
 	virtual void Initialise();
 	virtual void SwitchMode(CameraMode const &camera_mode, Metadata *metadata);
 	virtual void Prepare(Metadata *image_metadata);
diff --git a/src/ipa/raspberrypi/controller/controller.cpp b/src/ipa/raspberrypi/controller/controller.cpp
index d3433ad2e7e8..67d650ef0c1b 100644
--- a/src/ipa/raspberrypi/controller/controller.cpp
+++ b/src/ipa/raspberrypi/controller/controller.cpp
@@ -5,14 +5,16 @@
  * controller.cpp - ISP controller
  */
 
+#include <assert.h>
+
+#include <libcamera/base/file.h>
 #include <libcamera/base/log.h>
 
+#include "libcamera/internal/yaml_parser.h"
+
 #include "algorithm.hpp"
 #include "controller.hpp"
 
-#include <boost/property_tree/json_parser.hpp>
-#include <boost/property_tree/ptree.hpp>
-
 using namespace RPiController;
 using namespace libcamera;
 
@@ -32,16 +34,23 @@ Controller::~Controller() {}
 
 void Controller::Read(char const *filename)
 {
-	boost::property_tree::ptree root;
-	boost::property_tree::read_json(filename, root);
-	for (auto const &key_and_value : root) {
-		Algorithm *algo = CreateAlgorithm(key_and_value.first.c_str());
+	File file(filename);
+	if (!file.open(File::OpenModeFlag::ReadOnly)) {
+		LOG(RPiController, Warning)
+			<< "Failed to open tuning file '" << filename << "'";
+		return;
+	}
+
+	std::unique_ptr<YamlObject> root = YamlParser::parse(file);
+
+	for (auto const &[key, value] : root->asDict()) {
+		Algorithm *algo = CreateAlgorithm(key.c_str());
 		if (algo) {
-			algo->Read(key_and_value.second);
+			algo->Read(value);
 			algorithms_.push_back(AlgorithmPtr(algo));
 		} else
 			LOG(RPiController, Warning)
-				<< "No algorithm found for \"" << key_and_value.first << "\"";
+				<< "No algorithm found for \"" << key << "\"";
 	}
 }
 
diff --git a/src/ipa/raspberrypi/controller/pwl.cpp b/src/ipa/raspberrypi/controller/pwl.cpp
index 130c820b559f..9c7bc94dd484 100644
--- a/src/ipa/raspberrypi/controller/pwl.cpp
+++ b/src/ipa/raspberrypi/controller/pwl.cpp
@@ -12,13 +12,15 @@
 
 using namespace RPiController;
 
-void Pwl::Read(boost::property_tree::ptree const &params)
+void Pwl::Read(const libcamera::YamlObject &params)
 {
-	for (auto it = params.begin(); it != params.end(); it++) {
-		double x = it->second.get_value<double>();
-		assert(it == params.begin() || x > points_.back().x);
+	const auto &list = params.asList();
+
+	for (auto it = list.begin(); it != list.end(); it++) {
+		double x = it->get<double>(0.0);
+		assert(it == list.begin() || x > points_.back().x);
 		it++;
-		double y = it->second.get_value<double>();
+		double y = it->get<double>(0.0);
 		points_.push_back(Point(x, y));
 	}
 	assert(points_.size() >= 2);
diff --git a/src/ipa/raspberrypi/controller/pwl.hpp b/src/ipa/raspberrypi/controller/pwl.hpp
index 484672f64095..70df4ba0daea 100644
--- a/src/ipa/raspberrypi/controller/pwl.hpp
+++ b/src/ipa/raspberrypi/controller/pwl.hpp
@@ -6,10 +6,11 @@
  */
 #pragma once
 
+#include <functional>
 #include <math.h>
 #include <vector>
 
-#include <boost/property_tree/ptree.hpp>
+#include "libcamera/internal/yaml_parser.h"
 
 namespace RPiController {
 
@@ -55,7 +56,7 @@ public:
 	};
 	Pwl() {}
 	Pwl(std::vector<Point> const &points) : points_(points) {}
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 	void Append(double x, double y, const double eps = 1e-6);
 	void Prepend(double x, double y, const double eps = 1e-6);
 	Interval Domain() const;
diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp
index f6a9cb0a2cd8..c82f29600be7 100644
--- a/src/ipa/raspberrypi/controller/rpi/agc.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp
@@ -30,13 +30,13 @@ LOG_DEFINE_CATEGORY(RPiAgc)
 
 #define PIPELINE_BITS 13 // seems to be a 13-bit pipeline
 
-void AgcMeteringMode::Read(boost::property_tree::ptree const &params)
+void AgcMeteringMode::Read(const libcamera::YamlObject &params)
 {
 	int num = 0;
-	for (auto &p : params.get_child("weights")) {
+	for (const auto &p : params["weights"].asList()) {
 		if (num == AGC_STATS_SIZE)
 			throw std::runtime_error("AgcConfig: too many weights");
-		weights[num++] = p.second.get_value<double>();
+		weights[num++] = p.get<double>(0.0);
 	}
 	if (num != AGC_STATS_SIZE)
 		throw std::runtime_error("AgcConfig: insufficient weights");
@@ -44,39 +44,39 @@ void AgcMeteringMode::Read(boost::property_tree::ptree const &params)
 
 static std::string
 read_metering_modes(std::map<std::string, AgcMeteringMode> &metering_modes,
-		    boost::property_tree::ptree const &params)
+		    const libcamera::YamlObject &params)
 {
 	std::string first;
-	for (auto &p : params) {
+	for (const auto &[key, value] : params.asDict()) {
 		AgcMeteringMode metering_mode;
-		metering_mode.Read(p.second);
-		metering_modes[p.first] = std::move(metering_mode);
+		metering_mode.Read(value);
+		metering_modes[key] = std::move(metering_mode);
 		if (first.empty())
-			first = p.first;
+			first = key;
 	}
 	return first;
 }
 
 static int read_list(std::vector<double> &list,
-		     boost::property_tree::ptree const &params)
+		     const libcamera::YamlObject &params)
 {
-	for (auto &p : params)
-		list.push_back(p.second.get_value<double>());
+	for (const auto &p : params.asList())
+		list.push_back(p.get<double>(0.0));
 	return list.size();
 }
 
 static int read_list(std::vector<Duration> &list,
-		     boost::property_tree::ptree const &params)
+		     const libcamera::YamlObject &params)
 {
-	for (auto &p : params)
-		list.push_back(p.second.get_value<double>() * 1us);
+	for (const auto &p : params.asList())
+		list.push_back(p.get<double>(0.0) * 1us);
 	return list.size();
 }
 
-void AgcExposureMode::Read(boost::property_tree::ptree const &params)
+void AgcExposureMode::Read(const libcamera::YamlObject &params)
 {
-	int num_shutters = read_list(shutter, params.get_child("shutter"));
-	int num_ags = read_list(gain, params.get_child("gain"));
+	int num_shutters = read_list(shutter, params["shutter"]);
+	int num_ags = read_list(gain, params["gain"]);
 	if (num_shutters < 2 || num_ags < 2)
 		throw std::runtime_error(
 			"AgcConfig: must have at least two entries in exposure profile");
@@ -87,40 +87,40 @@ void AgcExposureMode::Read(boost::property_tree::ptree const &params)
 
 static std::string
 read_exposure_modes(std::map<std::string, AgcExposureMode> &exposure_modes,
-		    boost::property_tree::ptree const &params)
+		    const libcamera::YamlObject &params)
 {
 	std::string first;
-	for (auto &p : params) {
+	for (const auto &[key, value] : params.asDict()) {
 		AgcExposureMode exposure_mode;
-		exposure_mode.Read(p.second);
-		exposure_modes[p.first] = std::move(exposure_mode);
+		exposure_mode.Read(value);
+		exposure_modes[key] = std::move(exposure_mode);
 		if (first.empty())
-			first = p.first;
+			first = key;
 	}
 	return first;
 }
 
-void AgcConstraint::Read(boost::property_tree::ptree const &params)
+void AgcConstraint::Read(const libcamera::YamlObject &params)
 {
-	std::string bound_string = params.get<std::string>("bound", "");
+	std::string bound_string = params["bound"].get<std::string>("");
 	transform(bound_string.begin(), bound_string.end(),
 		  bound_string.begin(), ::toupper);
 	if (bound_string != "UPPER" && bound_string != "LOWER")
 		throw std::runtime_error(
 			"AGC constraint type should be UPPER or LOWER");
 	bound = bound_string == "UPPER" ? Bound::UPPER : Bound::LOWER;
-	q_lo = params.get<double>("q_lo");
-	q_hi = params.get<double>("q_hi");
-	Y_target.Read(params.get_child("y_target"));
+	q_lo = params["q_lo"].get<double>(0.0);
+	q_hi = params["q_hi"].get<double>(0.0);
+	Y_target.Read(params["y_target"]);
 }
 
 static AgcConstraintMode
-read_constraint_mode(boost::property_tree::ptree const &params)
+read_constraint_mode(const libcamera::YamlObject &params)
 {
 	AgcConstraintMode mode;
-	for (auto &p : params) {
+	for (const auto &p : params.asList()) {
 		AgcConstraint constraint;
-		constraint.Read(p.second);
+		constraint.Read(p);
 		mode.push_back(std::move(constraint));
 	}
 	return mode;
@@ -128,36 +128,36 @@ read_constraint_mode(boost::property_tree::ptree const &params)
 
 static std::string read_constraint_modes(
 	std::map<std::string, AgcConstraintMode> &constraint_modes,
-	boost::property_tree::ptree const &params)
+	const libcamera::YamlObject &params)
 {
 	std::string first;
-	for (auto &p : params) {
-		constraint_modes[p.first] = read_constraint_mode(p.second);
+	for (const auto &[key, value] : params.asDict()) {
+		constraint_modes[key] = read_constraint_mode(value);
 		if (first.empty())
-			first = p.first;
+			first = key;
 	}
 	return first;
 }
 
-void AgcConfig::Read(boost::property_tree::ptree const &params)
+void AgcConfig::Read(const libcamera::YamlObject &params)
 {
 	LOG(RPiAgc, Debug) << "AgcConfig";
 	default_metering_mode = read_metering_modes(
-		metering_modes, params.get_child("metering_modes"));
+		metering_modes, params["metering_modes"]);
 	default_exposure_mode = read_exposure_modes(
-		exposure_modes, params.get_child("exposure_modes"));
+		exposure_modes, params["exposure_modes"]);
 	default_constraint_mode = read_constraint_modes(
-		constraint_modes, params.get_child("constraint_modes"));
-	Y_target.Read(params.get_child("y_target"));
-	speed = params.get<double>("speed", 0.2);
-	startup_frames = params.get<uint16_t>("startup_frames", 10);
-	convergence_frames = params.get<unsigned int>("convergence_frames", 6);
+		constraint_modes, params["constraint_modes"]);
+	Y_target.Read(params["y_target"]);
+	speed = params["speed"].get<double>(0.2);
+	startup_frames = params["startup_frames"].get<uint16_t>(10);
+	convergence_frames = params["convergence_frames"].get<unsigned int>(6);
 	fast_reduce_threshold =
-		params.get<double>("fast_reduce_threshold", 0.4);
-	base_ev = params.get<double>("base_ev", 1.0);
+		params["fast_reduce_threshold"].get<double>(0.4);
+	base_ev = params["base_ev"].get<double>(1.0);
 	// Start with quite a low value as ramping up is easier than ramping down.
-	default_exposure_time = params.get<double>("default_exposure_time", 1000) * 1us;
-	default_analogue_gain = params.get<double>("default_analogue_gain", 1.0);
+	default_exposure_time = params["default_exposure_time"].get<double>(1000) * 1us;
+	default_analogue_gain = params["default_analogue_gain"].get<double>(1.0);
 }
 
 Agc::ExposureValues::ExposureValues()
@@ -186,7 +186,7 @@ char const *Agc::Name() const
 	return NAME;
 }
 
-void Agc::Read(boost::property_tree::ptree const &params)
+void Agc::Read(const libcamera::YamlObject &params)
 {
 	LOG(RPiAgc, Debug) << "Agc";
 	config_.Read(params);
diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp
index c100d3128c90..7794ba744efc 100644
--- a/src/ipa/raspberrypi/controller/rpi/agc.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp
@@ -26,13 +26,13 @@ namespace RPiController {
 
 struct AgcMeteringMode {
 	double weights[AGC_STATS_SIZE];
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 };
 
 struct AgcExposureMode {
 	std::vector<libcamera::utils::Duration> shutter;
 	std::vector<double> gain;
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 };
 
 struct AgcConstraint {
@@ -41,13 +41,13 @@ struct AgcConstraint {
 	double q_lo;
 	double q_hi;
 	Pwl Y_target;
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 };
 
 typedef std::vector<AgcConstraint> AgcConstraintMode;
 
 struct AgcConfig {
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 	std::map<std::string, AgcMeteringMode> metering_modes;
 	std::map<std::string, AgcExposureMode> exposure_modes;
 	std::map<std::string, AgcConstraintMode> constraint_modes;
@@ -72,7 +72,7 @@ class Agc : public AgcAlgorithm
 public:
 	Agc(Controller *controller);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	// AGC handles "pausing" for itself.
 	bool IsPaused() const override;
 	void Pause() override;
diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp
index e575c14a92db..b38b037c7713 100644
--- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp
@@ -5,6 +5,7 @@
  * alsc.cpp - ALSC (auto lens shading correction) control algorithm
  */
 
+#include <functional>
 #include <math.h>
 #include <numeric>
 
@@ -50,12 +51,12 @@ char const *Alsc::Name() const
 	return NAME;
 }
 
-static void generate_lut(double *lut, boost::property_tree::ptree const &params)
+static void generate_lut(double *lut, const libcamera::YamlObject &params)
 {
-	double cstrength = params.get<double>("corner_strength", 2.0);
+	double cstrength = params["corner_strength"].get<double>(2.0);
 	if (cstrength <= 1.0)
 		throw std::runtime_error("Alsc: corner_strength must be > 1.0");
-	double asymmetry = params.get<double>("asymmetry", 1.0);
+	double asymmetry = params["asymmetry"].get<double>(1.0);
 	if (asymmetry < 0)
 		throw std::runtime_error("Alsc: asymmetry must be >= 0");
 	double f1 = cstrength - 1, f2 = 1 + sqrt(cstrength);
@@ -73,50 +74,43 @@ static void generate_lut(double *lut, boost::property_tree::ptree const &params)
 	}
 }
 
-static void read_lut(double *lut, boost::property_tree::ptree const &params)
+static void read_lut(double *lut, const libcamera::YamlObject &params)
 {
+	if (params.size() != XY)
+		throw std::runtime_error(
+			"Alsc: invalid number of entries in LSC table");
+
 	int num = 0;
-	const int max_num = XY;
-	for (auto &p : params) {
-		if (num == max_num)
-			throw std::runtime_error(
-				"Alsc: too many entries in LSC table");
-		lut[num++] = p.second.get_value<double>();
-	}
-	if (num < max_num)
-		throw std::runtime_error("Alsc: too few entries in LSC table");
+	for (const auto &p : params.asList())
+		lut[num++] = p.get<double>(0.0);
 }
 
 static void read_calibrations(std::vector<AlscCalibration> &calibrations,
-			      boost::property_tree::ptree const &params,
+			      const libcamera::YamlObject &params,
 			      std::string const &name)
 {
-	if (params.get_child_optional(name)) {
+	if (params.contains(name)) {
 		double last_ct = 0;
-		for (auto &p : params.get_child(name)) {
-			double ct = p.second.get<double>("ct");
+		for (const auto &p : params[name].asList()) {
+			double ct = p["ct"].get<double>(0.0);
 			if (ct <= last_ct)
 				throw std::runtime_error(
 					"Alsc: entries in " + name +
 					" must be in increasing ct order");
 			AlscCalibration calibration;
 			calibration.ct = last_ct = ct;
-			boost::property_tree::ptree const &table =
-				p.second.get_child("table");
+
+			const libcamera::YamlObject &table = p["table"];
+			if (table.size() != XY)
+				throw std::runtime_error(
+					"Alsc: incorrect number of values for ct " +
+					std::to_string(ct) + " in " +
+					name);
+
 			int num = 0;
-			for (auto it = table.begin(); it != table.end(); it++) {
-				if (num == XY)
-					throw std::runtime_error(
-						"Alsc: too many values for ct " +
-						std::to_string(ct) + " in " +
-						name);
-				calibration.table[num++] =
-					it->second.get_value<double>();
-			}
-			if (num != XY)
-				throw std::runtime_error(
-					"Alsc: too few values for ct " +
-					std::to_string(ct) + " in " + name);
+			for (const auto &value : table.asList())
+				calibration.table[num++] = value.get<double>(0.0);
+
 			calibrations.push_back(calibration);
 			LOG(RPiAlsc, Debug)
 				<< "Read " << name << " calibration for ct " << ct;
@@ -124,35 +118,35 @@ static void read_calibrations(std::vector<AlscCalibration> &calibrations,
 	}
 }
 
-void Alsc::Read(boost::property_tree::ptree const &params)
+void Alsc::Read(const libcamera::YamlObject &params)
 {
-	config_.frame_period = params.get<uint16_t>("frame_period", 12);
-	config_.startup_frames = params.get<uint16_t>("startup_frames", 10);
-	config_.speed = params.get<double>("speed", 0.05);
-	double sigma = params.get<double>("sigma", 0.01);
-	config_.sigma_Cr = params.get<double>("sigma_Cr", sigma);
-	config_.sigma_Cb = params.get<double>("sigma_Cb", sigma);
-	config_.min_count = params.get<double>("min_count", 10.0);
-	config_.min_G = params.get<uint16_t>("min_G", 50);
-	config_.omega = params.get<double>("omega", 1.3);
-	config_.n_iter = params.get<uint32_t>("n_iter", X + Y);
+	config_.frame_period = params["frame_period"].get<uint16_t>(12);
+	config_.startup_frames = params["startup_frames"].get<uint16_t>(10);
+	config_.speed = params["speed"].get<double>(0.05);
+	double sigma = params["sigma"].get<double>(0.01);
+	config_.sigma_Cr = params["sigma_Cr"].get<double>(sigma);
+	config_.sigma_Cb = params["sigma_Cb"].get<double>(sigma);
+	config_.min_count = params["min_count"].get<double>(10.0);
+	config_.min_G = params["min_G"].get<uint16_t>(50);
+	config_.omega = params["omega"].get<double>(1.3);
+	config_.n_iter = params["n_iter"].get<uint32_t>(X + Y);
 	config_.luminance_strength =
-		params.get<double>("luminance_strength", 1.0);
+		params["luminance_strength"].get<double>(1.0);
 	for (int i = 0; i < XY; i++)
 		config_.luminance_lut[i] = 1.0;
-	if (params.get_child_optional("corner_strength"))
+	if (params.contains("corner_strength"))
 		generate_lut(config_.luminance_lut, params);
-	else if (params.get_child_optional("luminance_lut"))
+	else if (params.contains("luminance_lut"))
 		read_lut(config_.luminance_lut,
-			 params.get_child("luminance_lut"));
+			 params["luminance_lut"]);
 	else
 		LOG(RPiAlsc, Warning)
 			<< "no luminance table - assume unity everywhere";
 	read_calibrations(config_.calibrations_Cr, params, "calibrations_Cr");
 	read_calibrations(config_.calibrations_Cb, params, "calibrations_Cb");
-	config_.default_ct = params.get<double>("default_ct", 4500.0);
-	config_.threshold = params.get<double>("threshold", 1e-3);
-	config_.lambda_bound = params.get<double>("lambda_bound", 0.05);
+	config_.default_ct = params["default_ct"].get<double>(4500.0);
+	config_.threshold = params["threshold"].get<double>(1e-3);
+	config_.lambda_bound = params["lambda_bound"].get<double>(0.05);
 }
 
 static double get_ct(Metadata *metadata, double default_ct);
diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.hpp b/src/ipa/raspberrypi/controller/rpi/alsc.hpp
index d1dbe0d1d22d..9d28c1b49a6d 100644
--- a/src/ipa/raspberrypi/controller/rpi/alsc.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/alsc.hpp
@@ -52,7 +52,7 @@ public:
 	char const *Name() const override;
 	void Initialise() override;
 	void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Prepare(Metadata *image_metadata) override;
 	void Process(StatisticsPtr &stats, Metadata *image_metadata) override;
 
diff --git a/src/ipa/raspberrypi/controller/rpi/awb.cpp b/src/ipa/raspberrypi/controller/rpi/awb.cpp
index d4c934473832..1c40bf878cd3 100644
--- a/src/ipa/raspberrypi/controller/rpi/awb.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/awb.cpp
@@ -5,6 +5,8 @@
  * awb.cpp - AWB control algorithm
  */
 
+#include <assert.h>
+
 #include <libcamera/base/log.h>
 
 #include "../lux_status.h"
@@ -24,33 +26,35 @@ LOG_DEFINE_CATEGORY(RPiAwb)
 // todo - the locking in this algorithm needs some tidying up as has been done
 // elsewhere (ALSC and AGC).
 
-void AwbMode::Read(boost::property_tree::ptree const &params)
+void AwbMode::Read(const libcamera::YamlObject &params)
 {
-	ct_lo = params.get<double>("lo");
-	ct_hi = params.get<double>("hi");
+	ct_lo = params["lo"].get<double>(0.0);
+	ct_hi = params["hi"].get<double>(0.0);
 }
 
-void AwbPrior::Read(boost::property_tree::ptree const &params)
+void AwbPrior::Read(const libcamera::YamlObject &params)
 {
-	lux = params.get<double>("lux");
-	prior.Read(params.get_child("prior"));
+	lux = params["lux"].get<double>(0.0);
+	prior.Read(params["prior"]);
 }
 
 static void read_ct_curve(Pwl &ct_r, Pwl &ct_b,
-			  boost::property_tree::ptree const &params)
+			  const libcamera::YamlObject &params)
 {
+	const auto &list = params.asList();
 	int num = 0;
-	for (auto it = params.begin(); it != params.end(); it++) {
-		double ct = it->second.get_value<double>();
-		assert(it == params.begin() || ct != ct_r.Domain().end);
-		if (++it == params.end())
+
+	for (auto it = list.begin(); it != list.end(); it++) {
+		double ct = it->get<double>(0.0);
+		assert(it == list.begin() || ct != ct_r.Domain().end);
+		if (++it == list.end())
 			throw std::runtime_error(
 				"AwbConfig: incomplete CT curve entry");
-		ct_r.Append(ct, it->second.get_value<double>());
-		if (++it == params.end())
+		ct_r.Append(ct, it->get<double>(0.0));
+		if (++it == list.end())
 			throw std::runtime_error(
 				"AwbConfig: incomplete CT curve entry");
-		ct_b.Append(ct, it->second.get_value<double>());
+		ct_b.Append(ct, it->get<double>(0.0));
 		num++;
 	}
 	if (num < 2)
@@ -58,19 +62,19 @@ static void read_ct_curve(Pwl &ct_r, Pwl &ct_b,
 			"AwbConfig: insufficient points in CT curve");
 }
 
-void AwbConfig::Read(boost::property_tree::ptree const &params)
+void AwbConfig::Read(const libcamera::YamlObject &params)
 {
-	bayes = params.get<int>("bayes", 1);
-	frame_period = params.get<uint16_t>("frame_period", 10);
-	startup_frames = params.get<uint16_t>("startup_frames", 10);
-	convergence_frames = params.get<unsigned int>("convergence_frames", 3);
-	speed = params.get<double>("speed", 0.05);
-	if (params.get_child_optional("ct_curve"))
-		read_ct_curve(ct_r, ct_b, params.get_child("ct_curve"));
-	if (params.get_child_optional("priors")) {
-		for (auto &p : params.get_child("priors")) {
+	bayes = params["bayes"].get<int>(1);
+	frame_period = params["frame_period"].get<uint16_t>(10);
+	startup_frames = params["startup_frames"].get<uint16_t>(10);
+	convergence_frames = params["convergence_frames"].get<unsigned int>(3);
+	speed = params["speed"].get<double>(0.05);
+	if (params.contains("ct_curve"))
+		read_ct_curve(ct_r, ct_b, params["ct_curve"]);
+	if (params.contains("priors")) {
+		for (const auto &p : params["priors"].asList()) {
 			AwbPrior prior;
-			prior.Read(p.second);
+			prior.Read(p);
 			if (!priors.empty() && prior.lux <= priors.back().lux)
 				throw std::runtime_error(
 					"AwbConfig: Prior must be ordered in increasing lux value");
@@ -80,28 +84,28 @@ void AwbConfig::Read(boost::property_tree::ptree const &params)
 			throw std::runtime_error(
 				"AwbConfig: no AWB priors configured");
 	}
-	if (params.get_child_optional("modes")) {
-		for (auto &p : params.get_child("modes")) {
-			modes[p.first].Read(p.second);
+	if (params.contains("modes")) {
+		for (const auto &[key, value] : params["modes"].asDict()) {
+			modes[key].Read(value);
 			if (default_mode == nullptr)
-				default_mode = &modes[p.first];
+				default_mode = &modes[key];
 		}
 		if (default_mode == nullptr)
 			throw std::runtime_error(
 				"AwbConfig: no AWB modes configured");
 	}
-	min_pixels = params.get<double>("min_pixels", 16.0);
-	min_G = params.get<uint16_t>("min_G", 32);
-	min_regions = params.get<uint32_t>("min_regions", 10);
-	delta_limit = params.get<double>("delta_limit", 0.2);
-	coarse_step = params.get<double>("coarse_step", 0.2);
-	transverse_pos = params.get<double>("transverse_pos", 0.01);
-	transverse_neg = params.get<double>("transverse_neg", 0.01);
+	min_pixels = params["min_pixels"].get<double>(16.0);
+	min_G = params["min_G"].get<uint16_t>(32);
+	min_regions = params["min_regions"].get<uint32_t>(10);
+	delta_limit = params["delta_limit"].get<double>(0.2);
+	coarse_step = params["coarse_step"].get<double>(0.2);
+	transverse_pos = params["transverse_pos"].get<double>(0.01);
+	transverse_neg = params["transverse_neg"].get<double>(0.01);
 	if (transverse_pos <= 0 || transverse_neg <= 0)
 		throw std::runtime_error(
 			"AwbConfig: transverse_pos/neg must be > 0");
-	sensitivity_r = params.get<double>("sensitivity_r", 1.0);
-	sensitivity_b = params.get<double>("sensitivity_b", 1.0);
+	sensitivity_r = params["sensitivity_r"].get<double>(1.0);
+	sensitivity_b = params["sensitivity_b"].get<double>(1.0);
 	if (bayes) {
 		if (ct_r.Empty() || ct_b.Empty() || priors.empty() ||
 		    default_mode == nullptr) {
@@ -110,10 +114,9 @@ void AwbConfig::Read(boost::property_tree::ptree const &params)
 			bayes = false;
 		}
 	}
-	fast = params.get<int>(
-		"fast", bayes); // default to fast for Bayesian, otherwise slow
-	whitepoint_r = params.get<double>("whitepoint_r", 0.0);
-	whitepoint_b = params.get<double>("whitepoint_b", 0.0);
+	fast = params[fast].get<int>(bayes); // default to fast for Bayesian, otherwise slow
+	whitepoint_r = params["whitepoint_r"].get<double>(0.0);
+	whitepoint_b = params["whitepoint_b"].get<double>(0.0);
 	if (bayes == false)
 		sensitivity_r = sensitivity_b =
 			1.0; // nor do sensitivities make any sense
@@ -144,7 +147,7 @@ char const *Awb::Name() const
 	return NAME;
 }
 
-void Awb::Read(boost::property_tree::ptree const &params)
+void Awb::Read(const libcamera::YamlObject &params)
 {
 	config_.Read(params);
 }
diff --git a/src/ipa/raspberrypi/controller/rpi/awb.hpp b/src/ipa/raspberrypi/controller/rpi/awb.hpp
index ac3dca6f42fc..41334f798e2f 100644
--- a/src/ipa/raspberrypi/controller/rpi/awb.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/awb.hpp
@@ -19,20 +19,20 @@ namespace RPiController {
 // Control algorithm to perform AWB calculations.
 
 struct AwbMode {
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 	double ct_lo; // low CT value for search
 	double ct_hi; // high CT value for search
 };
 
 struct AwbPrior {
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 	double lux; // lux level
 	Pwl prior; // maps CT to prior log likelihood for this lux level
 };
 
 struct AwbConfig {
 	AwbConfig() : default_mode(nullptr) {}
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 	// Only repeat the AWB calculation every "this many" frames
 	uint16_t frame_period;
 	// number of initial frames for which speed taken as 1.0 (maximum)
@@ -82,7 +82,7 @@ public:
 	~Awb();
 	char const *Name() const override;
 	void Initialise() override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	// AWB handles "pausing" for itself.
 	bool IsPaused() const override;
 	void Pause() override;
diff --git a/src/ipa/raspberrypi/controller/rpi/black_level.cpp b/src/ipa/raspberrypi/controller/rpi/black_level.cpp
index 6b3497f13c19..de3a1e98ca1c 100644
--- a/src/ipa/raspberrypi/controller/rpi/black_level.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/black_level.cpp
@@ -31,13 +31,13 @@ char const *BlackLevel::Name() const
 	return NAME;
 }
 
-void BlackLevel::Read(boost::property_tree::ptree const &params)
+void BlackLevel::Read(const libcamera::YamlObject &params)
 {
-	uint16_t black_level = params.get<uint16_t>(
-		"black_level", 4096); // 64 in 10 bits scaled to 16 bits
-	black_level_r_ = params.get<uint16_t>("black_level_r", black_level);
-	black_level_g_ = params.get<uint16_t>("black_level_g", black_level);
-	black_level_b_ = params.get<uint16_t>("black_level_b", black_level);
+	// 64 in 10 bits scaled to 16 bits
+	uint16_t black_level = params["black_level"].get<uint16_t>(4096);
+	black_level_r_ = params["black_level_r"].get<uint16_t>(black_level);
+	black_level_g_ = params["black_level_g"].get<uint16_t>(black_level);
+	black_level_b_ = params["black_level_b"].get<uint16_t>(black_level);
 	LOG(RPiBlackLevel, Debug)
 		<< " Read black levels red " << black_level_r_
 		<< " green " << black_level_g_
diff --git a/src/ipa/raspberrypi/controller/rpi/black_level.hpp b/src/ipa/raspberrypi/controller/rpi/black_level.hpp
index 65ec4d0ed26c..5a63b5faef21 100644
--- a/src/ipa/raspberrypi/controller/rpi/black_level.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/black_level.hpp
@@ -18,7 +18,7 @@ class BlackLevel : public Algorithm
 public:
 	BlackLevel(Controller *controller);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Prepare(Metadata *image_metadata) override;
 
 private:
diff --git a/src/ipa/raspberrypi/controller/rpi/ccm.cpp b/src/ipa/raspberrypi/controller/rpi/ccm.cpp
index 821a4c7c98c5..0e02a524c68d 100644
--- a/src/ipa/raspberrypi/controller/rpi/ccm.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/ccm.cpp
@@ -37,17 +37,15 @@ Matrix::Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
 	m[0][0] = m0, m[0][1] = m1, m[0][2] = m2, m[1][0] = m3, m[1][1] = m4,
 	m[1][2] = m5, m[2][0] = m6, m[2][1] = m7, m[2][2] = m8;
 }
-void Matrix::Read(boost::property_tree::ptree const &params)
+void Matrix::Read(const libcamera::YamlObject &params)
 {
 	double *ptr = (double *)m;
-	int n = 0;
-	for (auto it = params.begin(); it != params.end(); it++) {
-		if (n++ == 9)
-			throw std::runtime_error("Ccm: too many values in CCM");
-		*ptr++ = it->second.get_value<double>();
-	}
-	if (n < 9)
-		throw std::runtime_error("Ccm: too few values in CCM");
+
+	if (params.size() != 9)
+		throw std::runtime_error("Ccm: wrong number of values in CCM");
+
+	for (const auto &param : params.asList())
+		*ptr++ = param.get<double>(0.0);
 }
 
 Ccm::Ccm(Controller *controller)
@@ -58,14 +56,14 @@ char const *Ccm::Name() const
 	return NAME;
 }
 
-void Ccm::Read(boost::property_tree::ptree const &params)
+void Ccm::Read(const libcamera::YamlObject &params)
 {
-	if (params.get_child_optional("saturation"))
-		config_.saturation.Read(params.get_child("saturation"));
-	for (auto &p : params.get_child("ccms")) {
+	if (params.contains("saturation"))
+		config_.saturation.Read(params["saturation"]);
+	for (auto &p : params["ccms"].asList()) {
 		CtCcm ct_ccm;
-		ct_ccm.ct = p.second.get<double>("ct");
-		ct_ccm.ccm.Read(p.second.get_child("ccm"));
+		ct_ccm.ct = p["ct"].get<double>(0.0);
+		ct_ccm.ccm.Read(p["ccm"]);
 		if (!config_.ccms.empty() &&
 		    ct_ccm.ct <= config_.ccms.back().ct)
 			throw std::runtime_error(
diff --git a/src/ipa/raspberrypi/controller/rpi/ccm.hpp b/src/ipa/raspberrypi/controller/rpi/ccm.hpp
index 330ed51fe398..073f02526850 100644
--- a/src/ipa/raspberrypi/controller/rpi/ccm.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/ccm.hpp
@@ -20,7 +20,7 @@ struct Matrix {
 	       double m6, double m7, double m8);
 	Matrix();
 	double m[3][3];
-	void Read(boost::property_tree::ptree const &params);
+	void Read(const libcamera::YamlObject &params);
 };
 static inline Matrix operator*(double d, Matrix const &m)
 {
@@ -62,7 +62,7 @@ class Ccm : public CcmAlgorithm
 public:
 	Ccm(Controller *controller = NULL);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void SetSaturation(double saturation) override;
 	void Initialise() override;
 	void Prepare(Metadata *image_metadata) override;
diff --git a/src/ipa/raspberrypi/controller/rpi/contrast.cpp b/src/ipa/raspberrypi/controller/rpi/contrast.cpp
index ae55aad56739..534f8b48a59b 100644
--- a/src/ipa/raspberrypi/controller/rpi/contrast.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/contrast.cpp
@@ -36,21 +36,21 @@ char const *Contrast::Name() const
 	return NAME;
 }
 
-void Contrast::Read(boost::property_tree::ptree const &params)
+void Contrast::Read(const libcamera::YamlObject &params)
 {
 	// enable adaptive enhancement by default
-	config_.ce_enable = params.get<int>("ce_enable", 1);
+	config_.ce_enable = params["ce_enable"].get<int>(1);
 	// the point near the bottom of the histogram to move
-	config_.lo_histogram = params.get<double>("lo_histogram", 0.01);
+	config_.lo_histogram = params["lo_histogram"].get<double>(0.01);
 	// where in the range to try and move it to
-	config_.lo_level = params.get<double>("lo_level", 0.015);
+	config_.lo_level = params["lo_level"].get<double>(0.015);
 	// but don't move by more than this
-	config_.lo_max = params.get<double>("lo_max", 500);
+	config_.lo_max = params["lo_max"].get<double>(500);
 	// equivalent values for the top of the histogram...
-	config_.hi_histogram = params.get<double>("hi_histogram", 0.95);
-	config_.hi_level = params.get<double>("hi_level", 0.95);
-	config_.hi_max = params.get<double>("hi_max", 2000);
-	config_.gamma_curve.Read(params.get_child("gamma_curve"));
+	config_.hi_histogram = params["hi_histogram"].get<double>(0.95);
+	config_.hi_level = params["hi_level"].get<double>(0.95);
+	config_.hi_max = params["hi_max"].get<double>(2000);
+	config_.gamma_curve.Read(params["gamma_curve"]);
 }
 
 void Contrast::SetBrightness(double brightness)
diff --git a/src/ipa/raspberrypi/controller/rpi/contrast.hpp b/src/ipa/raspberrypi/controller/rpi/contrast.hpp
index 85624539a1da..6b1e41724f5b 100644
--- a/src/ipa/raspberrypi/controller/rpi/contrast.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/contrast.hpp
@@ -32,7 +32,7 @@ class Contrast : public ContrastAlgorithm
 public:
 	Contrast(Controller *controller = NULL);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void SetBrightness(double brightness) override;
 	void SetContrast(double contrast) override;
 	void Initialise() override;
diff --git a/src/ipa/raspberrypi/controller/rpi/dpc.cpp b/src/ipa/raspberrypi/controller/rpi/dpc.cpp
index 110f50560e76..ac8aa78921c6 100644
--- a/src/ipa/raspberrypi/controller/rpi/dpc.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/dpc.cpp
@@ -29,9 +29,9 @@ char const *Dpc::Name() const
 	return NAME;
 }
 
-void Dpc::Read(boost::property_tree::ptree const &params)
+void Dpc::Read(const libcamera::YamlObject &params)
 {
-	config_.strength = params.get<int>("strength", 1);
+	config_.strength = params["strength"].get<int>(1);
 	if (config_.strength < 0 || config_.strength > 2)
 		throw std::runtime_error("Dpc: bad strength value");
 }
diff --git a/src/ipa/raspberrypi/controller/rpi/dpc.hpp b/src/ipa/raspberrypi/controller/rpi/dpc.hpp
index d90285c4eb56..382e20a6f1db 100644
--- a/src/ipa/raspberrypi/controller/rpi/dpc.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/dpc.hpp
@@ -22,7 +22,7 @@ class Dpc : public Algorithm
 public:
 	Dpc(Controller *controller);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Prepare(Metadata *image_metadata) override;
 
 private:
diff --git a/src/ipa/raspberrypi/controller/rpi/geq.cpp b/src/ipa/raspberrypi/controller/rpi/geq.cpp
index 4530cb75792c..cff3bfe01ed6 100644
--- a/src/ipa/raspberrypi/controller/rpi/geq.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/geq.cpp
@@ -33,14 +33,14 @@ char const *Geq::Name() const
 	return NAME;
 }
 
-void Geq::Read(boost::property_tree::ptree const &params)
+void Geq::Read(const libcamera::YamlObject &params)
 {
-	config_.offset = params.get<uint16_t>("offset", 0);
-	config_.slope = params.get<double>("slope", 0.0);
+	config_.offset = params["offset"].get<uint16_t>(0);
+	config_.slope = params["slope"].get<double>(0.0);
 	if (config_.slope < 0.0 || config_.slope >= 1.0)
 		throw std::runtime_error("Geq: bad slope value");
-	if (params.get_child_optional("strength"))
-		config_.strength.Read(params.get_child("strength"));
+	if (params.contains("strength"))
+		config_.strength.Read(params["strength"]);
 }
 
 void Geq::Prepare(Metadata *image_metadata)
diff --git a/src/ipa/raspberrypi/controller/rpi/geq.hpp b/src/ipa/raspberrypi/controller/rpi/geq.hpp
index 8ba3046b2a2b..84104be40452 100644
--- a/src/ipa/raspberrypi/controller/rpi/geq.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/geq.hpp
@@ -24,7 +24,7 @@ class Geq : public Algorithm
 public:
 	Geq(Controller *controller);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Prepare(Metadata *image_metadata) override;
 
 private:
diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp
index f77e9140ac10..cb93198d08a5 100644
--- a/src/ipa/raspberrypi/controller/rpi/lux.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/lux.cpp
@@ -36,14 +36,14 @@ char const *Lux::Name() const
 	return NAME;
 }
 
-void Lux::Read(boost::property_tree::ptree const &params)
+void Lux::Read(const libcamera::YamlObject &params)
 {
 	reference_shutter_speed_ =
-		params.get<double>("reference_shutter_speed") * 1.0us;
-	reference_gain_ = params.get<double>("reference_gain");
-	reference_aperture_ = params.get<double>("reference_aperture", 1.0);
-	reference_Y_ = params.get<double>("reference_Y");
-	reference_lux_ = params.get<double>("reference_lux");
+		params["reference_shutter_speed"].get<double>(0.0) * 1.0us;
+	reference_gain_ = params["reference_gain"].get<double>(0.0);
+	reference_aperture_ = params["reference_aperture"].get<double>(1.0);
+	reference_Y_ = params["reference_Y"].get<double>(0.0);
+	reference_lux_ = params["reference_lux"].get<double>(0.0);
 	current_aperture_ = reference_aperture_;
 }
 
diff --git a/src/ipa/raspberrypi/controller/rpi/lux.hpp b/src/ipa/raspberrypi/controller/rpi/lux.hpp
index 3ebd35d1e382..7d85199f7def 100644
--- a/src/ipa/raspberrypi/controller/rpi/lux.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/lux.hpp
@@ -22,7 +22,7 @@ class Lux : public Algorithm
 public:
 	Lux(Controller *controller);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Prepare(Metadata *image_metadata) override;
 	void Process(StatisticsPtr &stats, Metadata *image_metadata) override;
 	void SetCurrentAperture(double aperture);
diff --git a/src/ipa/raspberrypi/controller/rpi/noise.cpp b/src/ipa/raspberrypi/controller/rpi/noise.cpp
index 63cad639f313..66a2a7f3486c 100644
--- a/src/ipa/raspberrypi/controller/rpi/noise.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/noise.cpp
@@ -39,10 +39,10 @@ void Noise::SwitchMode(CameraMode const &camera_mode,
 	mode_factor_ = std::max(1.0, camera_mode.noise_factor);
 }
 
-void Noise::Read(boost::property_tree::ptree const &params)
+void Noise::Read(const libcamera::YamlObject &params)
 {
-	reference_constant_ = params.get<double>("reference_constant");
-	reference_slope_ = params.get<double>("reference_slope");
+	reference_constant_ = params["reference_constant"].get<double>(0.0);
+	reference_slope_ = params["reference_slope"].get<double>(0.0);
 }
 
 void Noise::Prepare(Metadata *image_metadata)
diff --git a/src/ipa/raspberrypi/controller/rpi/noise.hpp b/src/ipa/raspberrypi/controller/rpi/noise.hpp
index 1c9de5c87d08..353e79fa2a8c 100644
--- a/src/ipa/raspberrypi/controller/rpi/noise.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/noise.hpp
@@ -19,7 +19,7 @@ public:
 	Noise(Controller *controller);
 	char const *Name() const override;
 	void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Prepare(Metadata *image_metadata) override;
 
 private:
diff --git a/src/ipa/raspberrypi/controller/rpi/sdn.cpp b/src/ipa/raspberrypi/controller/rpi/sdn.cpp
index 9384550983e7..619a793cbdfb 100644
--- a/src/ipa/raspberrypi/controller/rpi/sdn.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/sdn.cpp
@@ -32,10 +32,10 @@ char const *Sdn::Name() const
 	return NAME;
 }
 
-void Sdn::Read(boost::property_tree::ptree const &params)
+void Sdn::Read(const libcamera::YamlObject &params)
 {
-	deviation_ = params.get<double>("deviation", 3.2);
-	strength_ = params.get<double>("strength", 0.75);
+	deviation_ = params["deviation"].get<double>(3.2);
+	strength_ = params["strength"].get<double>(0.75);
 }
 
 void Sdn::Initialise() {}
diff --git a/src/ipa/raspberrypi/controller/rpi/sdn.hpp b/src/ipa/raspberrypi/controller/rpi/sdn.hpp
index 2371ce04163f..e69557908cea 100644
--- a/src/ipa/raspberrypi/controller/rpi/sdn.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/sdn.hpp
@@ -18,7 +18,7 @@ class Sdn : public DenoiseAlgorithm
 public:
 	Sdn(Controller *controller = NULL);
 	char const *Name() const override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void Initialise() override;
 	void Prepare(Metadata *image_metadata) override;
 	void SetMode(DenoiseMode mode) override;
diff --git a/src/ipa/raspberrypi/controller/rpi/sharpen.cpp b/src/ipa/raspberrypi/controller/rpi/sharpen.cpp
index 18825a43867b..491f88d06c79 100644
--- a/src/ipa/raspberrypi/controller/rpi/sharpen.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/sharpen.cpp
@@ -37,11 +37,11 @@ void Sharpen::SwitchMode(CameraMode const &camera_mode,
 	mode_factor_ = std::max(1.0, camera_mode.noise_factor);
 }
 
-void Sharpen::Read(boost::property_tree::ptree const &params)
+void Sharpen::Read(const libcamera::YamlObject &params)
 {
-	threshold_ = params.get<double>("threshold", 1.0);
-	strength_ = params.get<double>("strength", 1.0);
-	limit_ = params.get<double>("limit", 1.0);
+	threshold_ = params["threshold"].get<double>(1.0);
+	strength_ = params["strength"].get<double>(1.0);
+	limit_ = params["limit"].get<double>(1.0);
 	LOG(RPiSharpen, Debug)
 		<< "Read threshold " << threshold_
 		<< " strength " << strength_
diff --git a/src/ipa/raspberrypi/controller/rpi/sharpen.hpp b/src/ipa/raspberrypi/controller/rpi/sharpen.hpp
index 13a076a86895..6dfc79afb0ac 100644
--- a/src/ipa/raspberrypi/controller/rpi/sharpen.hpp
+++ b/src/ipa/raspberrypi/controller/rpi/sharpen.hpp
@@ -19,7 +19,7 @@ public:
 	Sharpen(Controller *controller);
 	char const *Name() const override;
 	void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override;
-	void Read(boost::property_tree::ptree const &params) override;
+	void Read(const libcamera::YamlObject &params) override;
 	void SetStrength(double strength) override;
 	void Prepare(Metadata *image_metadata) override;
 
diff --git a/src/ipa/raspberrypi/meson.build b/src/ipa/raspberrypi/meson.build
index 32897e07dad9..517d815bb98c 100644
--- a/src/ipa/raspberrypi/meson.build
+++ b/src/ipa/raspberrypi/meson.build
@@ -4,7 +4,6 @@ ipa_name = 'ipa_rpi'
 
 rpi_ipa_deps = [
     libcamera_private,
-    dependency('boost'),
     libatomic,
 ]
 
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index 3b126bb5175e..f0f568d12513 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -7,6 +7,7 @@
 
 #include <algorithm>
 #include <array>
+#include <cstring>
 #include <fcntl.h>
 #include <math.h>
 #include <stdint.h>
