{"id":355,"date":"2024-05-24T13:50:21","date_gmt":"2024-05-24T12:50:21","guid":{"rendered":"https:\/\/eslsrv24.epfl.ch\/kid-ppg\/?page_id=355"},"modified":"2024-05-28T14:06:08","modified_gmt":"2024-05-28T13:06:08","slug":"the-kid-ppg-framework","status":"publish","type":"page","link":"https:\/\/eslweb.epfl.ch\/kid-ppg\/the-kid-ppg-framework\/","title":{"rendered":"The KID-PPG framework"},"content":{"rendered":"\n<p>At the heart of KID-PPG lie three key insights on PPG, Heart Rate and motion artifacts:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Motion artifact removal needs to be explicitely defined as a source separation task.<\/li>\n\n\n\n<li>In certain cases the PPG might be so corrupted by motion that it is infeasible to recover the heart rate from it &#8211; even if we have access to acceleration information through accelerometer sensors.<\/li>\n\n\n\n<li>The Blood Volume Pulse has specific characteristics. For example we saw in the introduction that a clean PPG signal is composed of the <strong><em>2 \u2022 HeartRate<\/em><\/strong> components.<\/li>\n<\/ol>\n\n\n\n<p>We have designed three mechanisms to integrate this <strong>prior-knowledge<\/strong> into KID-PPG:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Explicit Source Separation<\/li>\n\n\n\n<li>Probabilistic Inference and<\/li>\n\n\n\n<li>Guided Training<\/li>\n<\/ol>\n\n\n\n<p>We detail these three mechanisms below, providing also details on how our <a href=\"https:\/\/pypi.org\/project\/kid-ppg\/\">kid-ppg python package<\/a> can be used for direct heart rate estimation using these three key insights.<\/p>\n\n\n\n<p>Our python kid-ppg package is the first ever PPG-based heart rate extraction deep learning model with publicly available pretrained weights. You can easily install it and start analyzing PPG and acceleration data by simply downloading it through pip:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\npip install kid-ppg\n<\/pre><\/div>\n\n\n<p><strong>1. Explicit Source Separation<\/strong><\/p>\n\n\n\n<p>As we&#8217;ve seen, external motions can significantly damage the heart component in the PPG signal. In the case of periodic motions we can get some hints about the motion artifacts by observing the acceleration signals. In this case the 3D acceleration signal acts as a <strong>motion reference<\/strong> which we utilize to cancel the motion artifacts from the PPG.<\/p>\n\n\n\n<p>Unlike the SoA Deep Learning PPG models, which perform arbitrary data fusion, we propose an explicit source separation task. This way we enable the model to only focus on the most relevant parts of the signal &#8211; the Blood Volume Pulse.<\/p>\n\n\n\n<p>We chose to implement this source separation task as a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Adaptive_filter\">linear adaptive filter<\/a>. <code class=\"plain plain\">kid-ppg<\/code> provides the <code class=\"plain plain\">AdaptiveFilteringModel<\/code> for linear adaptive filtering. Calling an <code class=\"plain plain\">AdaptiveFilteringModel<\/code> model first trains the model adaptively based on the input PPG and accelerations. Then, it performs filtering based on the learned weights.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nimport tensorflow as tf\nfrom kid_ppg.preprocessing import sample_wise_z_score_normalization, sample_wise_z_score_denormalization\nfrom kid_ppg.adaptive_linear_model import AdaptiveFilteringModel\n\n# We define an optimizer which we will use to converge\n# the adaptive model.\nsgd = tf.keras.optimizers.legacy.SGD(learning_rate = 1e-7,\n                                            momentum = 1e-2,)\n# We train the model for 1000 epochs.\nn_epochs = 1000\nmodel = AdaptiveFilteringModel(local_optimizer = sgd,\n                               num_epochs_self_train = n_epochs)\n\n# We simultaneously perform model training and\n# filtering. X_input contains the PPG signals in\n# its first channel and 3 acceleration signals\n# in the rest 3 channels. Overall, X_input size\n# is &#x5B;N_samples, 256, 4].\nPPG_filtered = model(X_input&#x5B;..., None]).numpy()\n<\/pre><\/div>\n\n\n<p>The resulting <code>PPG_filtered<\/code> contains the the PPG signal after removing the motion artifacts. The results of our exploration showcase how this <strong>explicit source separation<\/strong> can significantly boost the performance of multiple deep learning models.<br><br><\/p>\n\n\n\n<div style=\"height:500px;\">\n      <object id=\"svg_object\" data=\"\/kid-ppg\/wp-content\/uploads\/2024\/05\/ma_removal_results_interactive.svg\" type=\"image\/svg+xml\" style=\"width: 500px; height:500px\"><\/object>\n<\/div>\n      <div>\n\n        <div style=\"display:flex;align-items: center;\">\n          <label class=\"switch\">\n              <input type=\"checkbox\" id=\"CleanPPG_toggle\" checked>\n              <span class=\"slider round\"><\/span>\n          <\/label>\n          <p style=\"padding-left:10px;font-size: 1.1em;\">Filtered PPG Signal<\/p>\n        <\/div>\n\n        <div style=\"display:flex;align-items: center;\">\n          <label class=\"switch\">\n              <input type=\"checkbox\" id=\"MAQPPG_toggle\" checked>\n              <span class=\"slider ma_ppg\"><\/span>\n          <\/label>\n          <p style=\"padding-left:10px;font-size: 1.1em;\">Motion Artifacts Q-PPG<\/p>\n        <\/div>\n\n        <div style=\"display:flex;align-items: center;\">\n          <label class=\"switch\">\n              <input type=\"checkbox\" id=\"CleanQPPG_toggle\" checked>\n              <span class=\"slider clean_ppg\"><\/span>\n          <\/label>\n          <p style=\"padding-left:10px;font-size: 1.1em;\">Filtered PPG Q-PPG<\/p>\n        <\/div>\n      <\/div>\n\n    <script>\n\n        var svgObject = document.getElementById(\"svg_object\");\n\n        svgObject.addEventListener(\"load\",function() {\n          var svgDoc = svgObject.contentDocument;\n          \n          let maqppg_inference = svgDoc.getElementById(\"path18-9\");\n          let cleanqppg_inference = svgDoc.getElementById(\"path18-2\");\n          let clean_ppg = svgDoc.getElementById(\"image201b490627\");\n\n          document.getElementById(\"MAQPPG_toggle\").addEventListener(\"change\", toggleMAQPPG_output);\n          document.getElementById(\"CleanQPPG_toggle\").addEventListener(\"change\", toggleCleanQPPG_output);\n          document.getElementById(\"CleanPPG_toggle\").addEventListener(\"change\", toggleCleanPPG_output);\n      \n          function toggleMAQPPG_output(){\n            let opacity = 0;\n            if (this.checked){\n              opacity = 1;\n            }\n            maqppg_inference.setAttribute('opacity', opacity);\n          }\n\n          function toggleCleanQPPG_output(){\n            let opacity = 0;\n            if (this.checked){\n              opacity = 1;\n            }\n            cleanqppg_inference.setAttribute('opacity', opacity);\n          }\n\n          function toggleCleanPPG_output(){\n            let opacity = 0;\n            if (this.checked){\n              opacity = 1;\n            }\n            clean_ppg.setAttribute('opacity', opacity);\n          }\n\n        }, false);\n      <\/script>\n\n\n\n<p><em>Figure 5: PPG and Heart Rate Extraction with and without adaptive linear filtering.<\/em><\/p>\n\n\n\n<p><strong>2. Probabilistic Inference<\/strong><\/p>\n\n\n\n<p>Unfortunately, acceleration is not always enough to cancel the effect of motion artifacts and get an accurate HR. In many cases, e.g. <strong>random motion<\/strong>, the acceleration does not provide any additional information for the motion. Additionally, the artifacts may be strong enough to completely corrupt the PPG signal beyond any capability of recovery. In <a href=\"https:\/\/infoscience.epfl.ch\/record\/310896?ln=en&amp;v=pdf\">our analysis<\/a> we have found out that for a significant portion of the PPGDalia dataset none of the currently available methods (Signal Processing or Deep models) can robustly recover the HR.<\/p>\n\n\n\n<p>In these cases, since the model does not have access to relevant information (destroyed BVP), any heart rate estimation might be <em>untrustworthy<\/em>. Preferably, we would like it to respond with <em>&#8220;I do not know, I cannot see the BVP clearly&#8221;<\/em> when asked about the heart rate.<\/p>\n\n\n\n<p>One way to introduce this <em>&#8220;I don&#8217;t know&#8221;<\/em> capability to the model is through <strong>probabilistic output<\/strong>. With a probabilistic output, the model does not output one single HR estimation, but rather a distribution of heart rates. An output distribution with a wide range of plausible heart rates (e.g. from 65 BPM to 150 BPM) indicates an uncertain HR estimation &#8211; the model cannot easily dissern the required BVP component in the input. Conversely, if the output heart rate distribution is concentrated around an HR value (e.g. from 65 BPM to 67 BPM), then the model is certain of its output.<\/p>\n\n\n\n<p>KID-PPG defines this distribution as a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Normal_distribution\">Gaussian<\/a>. This allows us to describe the distribution using only the expected output (most probable heart rate) and standard deviation (which relates to the range of probable heart rates): <strong><em>N(\u03bc<sub>hr<\/sub>,\u03c3<sup>2<\/sup><sub>hr<\/sub>). \u03bc<sub>hr<\/sub><\/em><\/strong> expresses the most probable heart rate, while <strong><em>\u03c3<sub>hr<\/sub><\/em><\/strong> is related to the spread of the range of probable heart rates.<\/p>\n\n\n\n<p>Let&#8217;s see an example of probabilistic inference on the examples of Figures 2 and 3:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img decoding=\"async\" src=\"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/ppg_sample_probabilistic.svg\" alt=\"\" class=\"wp-image-347\" style=\"width:638px;height:auto\"\/><\/figure>\n\n\n\n<p><em>Figure 4: Example of probabilistic inference on clean and motion-artifact affected signals. We present the energy of each sample as calculated by the Fourier transform. The range of possible heart rates, KID-PPG output, is presented as the shaded area. <strong>Left<\/strong>: KID-PPG is performing heart rate extraction on the clean sample of Figure 2. It has estimated that the heart rate is probably located around 82 BPM. The blue circle at 82.5 BPM indicates the true heart rate as measured by ECG. <strong>Right<\/strong>: We then perform probabilistic heart rate extraction in the corrupted sample of Figure 3. Since the PPG is seriously corrupt, KID-PPG outputs a large range of potential heart rates &#8211; essentially indicating <strong>&#8220;I don&#8217;t know&#8221;<\/strong>. The true heart rate is also presented with an orange circle. We also contrast KID-PPG output with the output of another deep learning model, which does not perform probabilistic inference (<a href=\"https:\/\/ieeexplore.ieee.org\/abstract\/document\/9583926\">Q-PPG<\/a>) with a black vertical line.<\/em><\/p>\n\n\n\n<p>We can easily perform probabilistic HR estimation using the <code>kid-ppg<\/code> package. All we need is to instantiate a <code>KID_PPG()<\/code> model and perform a prediction using the model&#8217;s <code>predict()<\/code> function:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nfrom kid_ppg.kid_ppg import KID_PPG\nfrom kid_ppg.preprocessing import create_temporal_pairs\n\n# The KID-PPG model receives inputs as pairs &#x5B;PPG(t - 1), PPG(t)]\n# The kid-ppg package provides a utility function to easily form\n# these pairs.\nX_filtered_temp, y_temp = create_temporal_pairs(X_filtered, y)\n\n# Instantiate a KID_PPG model.\nkid_ppg_model = KID_PPG()\n\n# Perform prediction on the temporal PPG samples.\nhr_pred_m, hr_pred_std = kid_ppg_model.predict(X_filtered_temp)\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>The <code>predict<\/code> function returns the expected HR values, <strong><em>\u03bc<sub>hr<\/sub><\/em><\/strong> and the standard deviations,&nbsp;<strong><em>\u03c3<sub>hr<\/sub><\/em><\/strong>, corresponding to the input PPG samples, <code>X_filtered_temp<\/code>.<\/p>\n\n\n\n<p>The standard deviation, <code>hr_pred_std<\/code>, provides a soft label which can be considered as the model&#8217;s certainty on its HR estimation. Using the standard deviation we can tell if one sample is more uncertain than another one. However, in many applications we want our model to not exceed a maximum error of heart rate estimation. KID-PPG allows to define such a threshold and retain only the samples which present a low probability of exceeding this error threshold.<\/p>\n\n\n\n<p>For example, let&#8217;s consider that for our application specifies a maximum estimation error of 10 BPM. We can use the output distribution, <strong><em>N(\u03bc<sub>hr<\/sub>,\u03c3<sup>2<\/sup><sub>hr<\/sub>)<\/em><\/strong> to classify each sample as &#8220;probably high-error&#8221; (probably <code>error &gt; 10<em>BPM<\/em><\/code>) or &#8220;probably low-error&#8221; (probably&nbsp; <code>error &lt; 10<em>BPM<\/em><\/code>). Similarly to <code>predict<\/code>, we use <code>predict_threshold<\/code>, but here we also define the <code>threshold<\/code> parameter:<\/p>\n\n\n\n<p>Along with the <code>hr_pred_m<\/code> and <code>hr_pred_std<\/code>, this time we also get <code>hr_pre_p<\/code>, which is the probability the estimation error will not exceed the defined <code>threshold<\/code>. We can then retain only the samples which have a <code>hr_pred_p<\/code> higher than 0.5.<\/p>\n\n\n\n<p><strong>3. Guided Training<\/strong><\/p>\n\n\n\n<p>Probabilistic output allows the model to output <em>&#8220;I don&#8217;t know&#8221;<\/em>. Without any guidance the model can converge to a lot of different rules on what it believes a <em>&#8220;clean&#8221;<\/em> sample or a damaged beyond any recovery are. For example if the majority of clean training samples are of low energy, then the model could learn the <a href=\"https:\/\/arxiv.org\/abs\/2004.07780\">shortcut<\/a>: <em>&#8220;When the PPG signal energy is low, I am certain&#8221;<\/em>. Of course, we know that this rule is not robust: low energy signal could still mean no Blood Volume Pulse in the input. Let&#8217;s do a small experiment, Figure 6. We select a clean PPG sample and corrupt it beyond any possibility for recovery by filtering-out the portion of the signal which corresponds to the heart rate:<\/p>\n\n\n\n<p>Let&#8217;s do a small experiment, Figure 6. We select a clean PPG sample and corrupt it beyond any possibility for recovery by filtering-out the portion of the signal which corresponds to the heart rate: energies at <strong><em>HeartRate, 2 \u2022 HeartRate<\/em><\/strong> and <strong><em>3 \u2022 HeartRate<\/em><\/strong>. Without any guidance during training, probabilistic model outputs a low-uncertainty output.<\/p>\n\n\n\n<p>We then boost KID-PPG&#8217;s robustness in uncertainty estimation. We guide the model to base its uncertainty based on our <strong>prior-knowledge<\/strong> of a clean PPG signal &#8211; containing the <strong><em>HeartRate<\/em><\/strong>, <strong><em>2 \u2022 HeartRate<\/em><\/strong> and maybe <strong><em>3 \u2022 HeartRate<\/em><\/strong> in very good cases. We achieve this by complementing the training dataset with synthetically corrupted realistic samples and guiding the model to be uncertain of them.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"533\" src=\"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-1024x533.gif\" alt=\"\" class=\"wp-image-403\" style=\"width:854px;height:auto\" srcset=\"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-1024x533.gif 1024w, https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-300x156.gif 300w, https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-768x400.gif 768w, https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-1000x521.gif 1000w, https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-1075x560.gif 1075w, https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure6_optimised-600x313.gif 600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><em>Figure 6: Robust uncertainty estimation can be benefited by guided training based on prior-knowledge on the expected morphology of clean PPG signals. <strong>Left:<\/strong> We corrupt a clean sample (grey) by removing the heart component out of it (black). <strong>Right:<\/strong> The energy vs frequency plots of the clean (grey) and corrupted signals (black). The ground-truth heart rate is presented with a green circle. The unguided model presents a low uncertainty (blue), even though the signal is heavily corrupted to the point of no reconstruction. In contrast, KID-PPG, which is guided during probabilistic training, is uncertain of its output.<\/em><\/p>\n\n\n\n<p>Our results indicate that guided probabilistic training boosts the model&#8217;s robustness in estimating the probability of its estimations&#8217; error.<\/p>\n\n\n\n<p>Our analysis has also revealed that the range of heart rates available in the training dataset will affect the end-result model. For example, let&#8217;s consider training data with HR in the range of [50, 150] BPM. A model trained on these data will not learn to identify faster heart rates, for example at 200 BPM. As far as this model is concerned, the HR is always in the range of [50, 150] BPM. However, through medical knowledge we are sure that human heart rate can exceed the 150 BPM limit of the training data. This is why in KID-PPG we make sure the training set contains samples from the entire range of possible human heart rates. Since real recorded data might not be able to provide this vast range, we have synthetically enriched the training set.<\/p>\n\n\n\n<p>Let&#8217;s see an example of the advantage of guiding the network to consider a wider range of heart rates.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img decoding=\"async\" width=\"543\" height=\"546\" src=\"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/figure7_optimised.gif\" alt=\"\" class=\"wp-image-401\" style=\"width:374px;height:auto\"\/><\/figure>\n\n\n\n<p><em>Figure 7: Demo of the effect of the heart rate range in the training samples. The Fourier transform of signal is presented in black and the green circle signifies the ground-truth heart rate. The frequency corresponding to the heart rate presents a distinguishable peak, and extracting the heart rate is straightforward for this sample. When trained on a limited range of heart rates (blue distribution) the model predicts a considerably lower heart rate (blue vertical line), even though it should be quite easy to detect the correct periodicity. We then synthetically expand the range of the original training set to include the entire range of humanly possible heart rates (orange distribution). The guided model then predicts correctly the heart rate.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Open Source Pretrained KID-PPG<\/h2>\n\n\n\n<p>You can directly use our pretrained KID-PPG model using our pypi package available <a href=\"https:\/\/pypi.org\/project\/kid-ppg\/\">here<\/a>. You can also check our KID-PPG <a href=\"https:\/\/infoscience.epfl.ch\/record\/310896?ln=en&amp;v=pdf\">manuscript<\/a>. Finally, check-out our end-to-end <code class=\"plain plain\">kid-ppg<\/code> workflow in <a href=\"https:\/\/colab.research.google.com\/drive\/1I7lP_elVuzf3sn2Tlm0QsgarUR0_9z-l?usp=share_link\">this<\/a> tutorial notebook.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>At the heart of KID-PPG lie three key insights on PPG, Heart Rate and motion artifacts: We have designed three mechanisms to integrate this prior-knowledge into KID-PPG: We detail these three mechanisms below, providing also details on how our kid-ppg python package can be used for direct heart rate estimation [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":458,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"inspiro_page_layout":"narrow","inspiro_hide_title":false,"footnotes":""},"class_list":["post-355","page","type-page","status-publish","has-post-thumbnail","hentry"],"featured_media_urls":{"thumbnail":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-150x150.jpg",150,150,true],"medium":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-300x168.jpg",300,168,true],"medium_large":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-768x431.jpg",768,431,true],"large":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-1024x574.jpg",930,521,true],"1536x1536":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-1536x861.jpg",1536,861,true],"2048x2048":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1600,897,false],"featured":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1600,897,false],"featured@2x":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1600,897,false],"featured-small":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-1000x561.jpg",1000,561,true],"recent-thumbnail":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-345x192.jpg",345,192,true],"recent-thumbnail-retina":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-690x384.jpg",690,384,true],"woo-featured":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-280x280.jpg",280,280,true],"entry-cover":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1600,897,false],"entry-cover@2x":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1600,897,false],"loop":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-950x320.jpg",950,320,true],"loop@2x":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1142,640,false],"portfolio-scroller-widget":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-999x560.jpg",999,560,true],"portfolio_item-thumbnail":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",600,336,false],"portfolio_item-thumbnail@2x":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1200,673,false],"portfolio_item-masonry":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",600,336,false],"portfolio_item-thumbnail_wide":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-1600x600.jpg",1600,600,true],"portfolio_item-thumbnail_wide_cinema":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375-1600x750.jpg",1600,750,true],"portfolio_item-thumbnail_cinema":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",598,335,false],"portfolio_item-thumbnail_portrait":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",600,336,false],"portfolio_item-thumbnail_square":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",800,449,false],"portfolio_item-masonry@2x":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1200,673,false],"portfolio_item-thumbnail_portrait@2x":["https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-content\/uploads\/2024\/05\/AdobeStock_731533375.jpg",1200,673,false]},"_links":{"self":[{"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/pages\/355","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/comments?post=355"}],"version-history":[{"count":43,"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/pages\/355\/revisions"}],"predecessor-version":[{"id":489,"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/pages\/355\/revisions\/489"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/media\/458"}],"wp:attachment":[{"href":"https:\/\/eslweb.epfl.ch\/kid-ppg\/wp-json\/wp\/v2\/media?parent=355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}