{"id":644,"date":"2025-04-12T12:00:08","date_gmt":"2025-04-12T19:00:08","guid":{"rendered":"https:\/\/www.ferzkopp.net\/wordpress\/?p=644"},"modified":"2025-04-12T14:24:21","modified_gmt":"2025-04-12T21:24:21","slug":"estimating-tree-age-with-image-analysis-and-stats","status":"publish","type":"post","link":"https:\/\/www.ferzkopp.net\/wordpress\/2025\/04\/12\/estimating-tree-age-with-image-analysis-and-stats\/","title":{"rendered":"Tree Age from Image Analysis and Statistics"},"content":{"rendered":"\n\n\n<div>\n<div>Can one use an image of tree rings to estimate its age by analyzing circular patterns and brightness variations? Let&#8217;s pull up Python and find out &#8230;<\/div>\n<div>&nbsp;<\/div>\n<\/div>\n<p><!--more--><\/p>\n\n\n\n<p>I found a nice snapshot of a cut tree that I took during a winter forest walk in my picture archive, and had the idea to automate the process of counting the tree rings in order to estimate the age of the tree when it was harvested.<\/p>\n<p><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-645 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-1024x831.png\" alt=\"\" width=\"750\" height=\"609\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-1024x831.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-128x104.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-1536x1246.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-2048x1661.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n<p>Using VS Code, GitHub Copilot and some time to tinker and experiment, I wrote the some code to perform this analysis. The resulting Python program applies image processing techniques, detects circular features, and performs statistical analysis on the detected markers for such an estimation. It is by no means perfect &#8211; specifically finding the center of the rings proved challenging and error prone. The resulting estimates are, well, approximate.<\/p>\n<p>The source code, input and process images, and additional samples to process can be downloaded here:<\/p>\n<p><a href=\"https:\/\/github.com\/ferzkopp\/TreeRingAnalysis\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/ferzkopp\/TreeRingAnalysis<\/a><\/p>\n\n\n\n<p>The program uses the following workflow:<\/p>\n<div>\n<div>&nbsp; &nbsp; 1. Load and preprocess the image (e.g., Gaussian blur, HSV conversion).<\/div>\n<div>&nbsp; &nbsp; 2. Perform color analysis and binarize the image based on percentile brightness thresholds.<\/div>\n<div>&nbsp; &nbsp; 3. Detect the center of circular features using gradient decent optimization.<\/div>\n<div>&nbsp; &nbsp; 4. Analyze pixel brightness along many radial lines from the detected center.<\/div>\n<div>&nbsp; &nbsp; 5. Apply frequency filtering and detect peaks\/troughs in the series of brightness values along the rays.<\/div>\n<div>&nbsp; &nbsp; 6. Visualize and save results, including marker overlays and statistical plots.<\/div>\n<div>&nbsp; &nbsp; 7. Estimate the tree&#8217;s age based on statistical metrics of the set of detected peaks\/troughs.<\/div>\n<\/div>\n\n\n\n<p><em>Let&#8217;s walk through each step.<\/em><\/p>\n\n\n\n<p>Since the source images may be small, the first step is an <strong>optional scaling<\/strong> operation to make sure it has&nbsp; at least 3000 pixels in width.&nbsp;<code><\/code><\/p>\n<div>\n<div><code>if image.shape[1] &lt; 3000:<\/code><\/div>\n<div><code>&nbsp; &nbsp; scale_factor = 3000 \/ image.shape[1]<\/code><\/div>\n<div><code>&nbsp; &nbsp; new_width = 3000<\/code><\/div>\n<div><code>&nbsp; &nbsp; new_height = int(image.shape[0] * scale_factor)<\/code><\/div>\n<div><code>  &nbsp; image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_CUBIC)<\/code><\/div>\n<\/div>\n<p>and to remove the noise some blurring is applied<\/p>\n<div>\n<div><code>blurred_image = image.copy()<\/code><\/div>\n<div><code>blurred_image = cv2.GaussianBlur(blurred_image, (7, 7), 0)<\/code><\/div>\n<div><code>blurred_image = cv2.GaussianBlur(blurred_image, (5, 5), 0)<\/code><\/div>\n<div><code>blurred_image = cv2.GaussianBlur(blurred_image, (3, 3), 0)<\/code><\/div>\n<\/div>\n<div>&nbsp;<\/div>\n<div>This image is then used for two processes: (a) finding an approximate center of the rings, and (b) analyzing the rings using rays emanating from this center.<\/div>\n\n\n\n<p>Finding the center of the concentric rings &#8211; a task that is easy for a human looking at the image of a tree &#8211; is surprisingly difficult for an algorithm due to the nature of the image with noise, low contrast, vertical variations, and other inconsistencies. I tried initially several algorithms suggested by a quick Copilot query such as Hough Transform, Radial Symmetry Transform and Template Matching before settling on a <strong>gradient decent search<\/strong> using a cost function that is designed to find the center of concentric structures (like tree rings) based on edge data in the image.<\/p>\n<div>\n<div><code># Cost function to evaluate the center<\/code><\/div>\n<div><code>def cost_function(center, edges):<\/code><\/div>\n<div><code>&nbsp; &nbsp; x_center, y_center = center<\/code><\/div>\n<div><code>&nbsp; &nbsp; y_indices, x_indices = np.where(edges &gt; 0) &nbsp;# Get edge points<\/code><\/div>\n<div><code>&nbsp; &nbsp; radii = np.sqrt((x_indices - x_center)**2 + (y_indices - y_center)**2)<\/code><\/div>\n<div><code>&nbsp; &nbsp; mean_radius = np.mean(radii)<\/code><\/div>\n<div><code>&nbsp; &nbsp; cost = np.sum((radii - mean_radius)**2) &nbsp;# Minimize the deviation from the mean radius<\/code><\/div>\n<div><code>&nbsp; &nbsp; return cost<\/code><\/div>\n<br>\n<div><code># Gradient descent to find the best center<\/code><\/div>\n<div><code>def find_best_center(edges, initial_center):<\/code><\/div>\n<div><code>&nbsp; &nbsp; result = minimize(<\/code><\/div>\n<div><code>&nbsp; &nbsp; &nbsp; &nbsp; cost_function,<\/code><\/div>\n<div><code>&nbsp; &nbsp; &nbsp; &nbsp; initial_center,<\/code><\/div>\n<div><code>&nbsp; &nbsp; &nbsp; &nbsp; args=(edges,),<\/code><\/div>\n<div><code>&nbsp; &nbsp; &nbsp; &nbsp; method='Powell'<\/code><\/div>\n<div><code>&nbsp; &nbsp; )<\/code><\/div>\n<div><code>&nbsp; &nbsp; return result.x<\/code><\/div>\n<div><br>The input to this step is a &#8220;binarized&#8221; image that shows the rings around the center as bright pixels. The following processing was performed to create such a black and white image:<\/div>\n<ul style=\"list-style-type: circle;\">\n<li><strong>cut out<\/strong> the center quarter of the image, since it is most likely to contain the center of the rings<br><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-CenterQuarterBoundingBox.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-653 size-medium\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-CenterQuarterBoundingBox-128x104.png\" alt=\"\" width=\"128\" height=\"104\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-CenterQuarterBoundingBox-128x104.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-CenterQuarterBoundingBox-1024x831.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-CenterQuarterBoundingBox-1536x1246.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-CenterQuarterBoundingBox-2048x1661.png 2048w\" sizes=\"auto, (max-width: 128px) 100vw, 128px\" \/><\/a><\/li>\n<li>convert the color image into <strong>HSV<\/strong> (Hue, Saturation, Value) format <br>calculate a <strong>histogram<\/strong> of the V component (brightness)<\/li>\n<li><strong>binarize<\/strong> values using a<strong> percentile threshol<\/strong>d (values below X<sup>th<\/sup>-percentile become white pixels)<\/li>\n<li><strong>mask<\/strong> out all parts not within a centered circle (black pixel)<\/li>\n<\/ul>\n<div><br>Here is an example of the binarized version of the above image source:<br><br><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-Binarized.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-649 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-Binarized-1024x831.png\" alt=\"\" width=\"750\" height=\"609\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-Binarized-1024x831.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-Binarized-128x104.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-Binarized-1536x1246.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-Binarized-2048x1661.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/div>\n<\/div>\n<div><br>and this is the result of the center search based on the masked version of this image:<br><br><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DetectedCenter.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-648 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DetectedCenter-1024x831.png\" alt=\"\" width=\"750\" height=\"609\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DetectedCenter-1024x831.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DetectedCenter-128x104.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DetectedCenter-1536x1246.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DetectedCenter-2048x1661.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/div>\n\n\n\n<p>The green dot indicates the initial estimate at the start of the search (image center) and the red dot indicates the final location (estimate of tree ring center). As one can see, this estimate is far from perfect, but sufficiently accurate to proceed.<\/p>\n<p>Now that we have a center, the next step of the algorithm can be applied: creating time-series of brightness values along rays emanating from the center. First a <strong>grayscale<\/strong> version of the source image is created and enhanced using <strong>histogram equalization<\/strong>.<\/p>\n<p><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-BlurredGrayForAnalysis.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-650 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-BlurredGrayForAnalysis-1024x831.png\" alt=\"\" width=\"750\" height=\"609\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-BlurredGrayForAnalysis-1024x831.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-BlurredGrayForAnalysis-128x104.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-BlurredGrayForAnalysis-1536x1246.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-BlurredGrayForAnalysis-2048x1661.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n\n\n\n<p>Then the image is sampled along 360 rays projecting from the center in 1 degree intervals to create 360 individual brightness series.<\/p>\n<p><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlay.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-655 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlay-1024x831.png\" alt=\"\" width=\"750\" height=\"609\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlay-1024x831.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlay-128x104.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlay-1536x1246.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlay-2048x1661.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n<p>Here is a close-up showing the dark (orange dots) and bright (blue dots) areas detected in the image.<\/p>\n<p><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlaySection.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-656 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlaySection-1024x452.png\" alt=\"\" width=\"750\" height=\"331\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlaySection-1024x452.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlaySection-128x56.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlaySection-1536x677.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerOverlaySection-2048x903.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n<p>Each brightness series is then processed as follows:<\/p>\n<ul style=\"list-style-type: square;\">\n<li>the series is <strong>frequency filtered<\/strong> with a low-pass filter to remove high frequency variations<\/li>\n<li>the filter is using a <strong>dynamic cutoff<\/strong>, filtering more close to the center (which has broader spaced rings) and less in the outer parts (which usually has tighter ring spacings) using a linear cutoff response<br><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DynamicCutoffResponse.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-652 size-medium\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DynamicCutoffResponse-128x97.png\" alt=\"\" width=\"128\" height=\"97\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DynamicCutoffResponse-128x97.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DynamicCutoffResponse-1024x779.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DynamicCutoffResponse-1536x1169.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-DynamicCutoffResponse.png 1789w\" sizes=\"auto, (max-width: 128px) 100vw, 128px\" \/><\/a><\/li>\n<li>then on to <strong>detect<\/strong> peaks and troughs in the resulting brightness curve, treating each peak (or trough) as a possible ring,<\/li>\n<li>and finally by collecting the <strong>count<\/strong> of the peaks (or troughs) in each series as an estimate along that particular ray<\/li>\n<\/ul>\n<p><code>fft_values = np.fft.fft(brightness_values)<\/code><br><code>frequencies = np.fft.fftfreq(len(brightness_values))<\/code><br><code>dynamic_cutoff = cutoff + (cutoff * 2) * (frequencies \/ max(frequencies))<\/code><br><code>fft_values[np.abs(frequencies) &gt; dynamic_cutoff] = 0<\/code><br><code>smoothed_brightness_values = np.fft.ifft(fft_values).real<\/code><br><code>troughs, _ = find_peaks(-smoothed_brightness_values, prominence=prominence) <\/code><br><code>peaks, _ = find_peaks(smoothed_brightness_values, prominence=prominence)<\/code><\/p>\n<p>The following chart shows the resulting <strong>peak and trough detection<\/strong> results for 3 rays (0\/45\/90 degrees):<\/p>\n<p><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerAnalysis.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-651 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerAnalysis-1024x817.png\" alt=\"\" width=\"750\" height=\"598\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerAnalysis-1024x817.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerAnalysis-128x102.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerAnalysis-1536x1225.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerAnalysis-2048x1634.png 2048w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n\n\n\n<p>The final step is to statistically analyze the set of peak and trough counts (collected as <em>markers<\/em>) that were found, 720 in total, to find a possible range for our tree age using a mean, mode and maxima in the set.<\/p>\n<div>\n<div>\n<div>\n<div><code>mean, std = norm.fit(marker_series) &nbsp;# Fit a Gaussian distribution<\/code><\/div>\n<div>\n<div>\n<div><code>mode_value, mode_count = mode(marker_series)<\/code><\/div>\n<div>\n<div>\n<div><code>max_value = max(marker_series)<\/code><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div><code>estimated_age_mean = int(mean)<\/code><\/div>\n<div><code>estimated_age_mode = int(mode_value)<\/code><\/div>\n<div><code>estimated_age_max = int(max_value)<\/code><\/div>\n<div><code>estimated_age_range = (min(estimated_age_mean, estimated_age_mode, estimated_age_max),<\/code><\/div>\n<div><code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;max(estimated_age_mean, estimated_age_mode, estimated_age_max))<\/code><\/div>\n<\/div>\n<div>&nbsp;<\/div>\n<div>The following histogram of the marker series illustrates these results:<\/div>\n<p><a href=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerHistogram.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-654 size-large\" src=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerHistogram-1024x815.png\" alt=\"\" width=\"750\" height=\"597\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerHistogram-1024x815.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerHistogram-128x102.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerHistogram-1536x1222.png 1536w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2025\/04\/Tree-MarkerHistogram.png 1728w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n\n\n\n<p>This statistical averaging &#8220;smooths&#8221; the imperfections in the algorithms. <\/p>\n\n\n\n<p>The program outputs the final estimate:<br><br><code>...<\/code><\/p>\n<p><code>The mean of the marker series is 55.<\/code><code><br>The mode of the marker series is 58 (with a count of 75).<br>The maximum value of the marker series is 67.<br><strong>The estimated age of the tree is between 55 and 67 years.<\/strong><\/code><\/p>\n\n\n\n<p>I counted the tree rings in the image by hand and estimate the actual age of the tree to be about <strong>72 years<\/strong>, so the algorithm was underestimating. However, even manually determining the ring count from the image is challenging and my own estimate may not be entirely correct. <br>Close enough for me. \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Can one use an image of tree rings to estimate its age by analyzing circular patterns and brightness variations? Let&#8217;s pull up Python and find out &#8230; &nbsp;<\/p>\n","protected":false},"author":1,"featured_media":645,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[76,15,22],"tags":[92,93],"class_list":["post-644","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-mathematics","category-personal","category-software","tag-python","tag-tree-ring-analysis"],"_links":{"self":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/comments?post=644"}],"version-history":[{"count":10,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/644\/revisions"}],"predecessor-version":[{"id":669,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/644\/revisions\/669"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/media\/645"}],"wp:attachment":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/media?parent=644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/categories?post=644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/tags?post=644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}