{"id":1896,"date":"2011-11-06T00:36:50","date_gmt":"2011-11-06T07:36:50","guid":{"rendered":"http:\/\/mcclanahoochie.com\/blog\/?post_type=portfolio&#038;p=1896"},"modified":"2023-06-10T10:32:43","modified_gmt":"2023-06-10T17:32:43","slug":"gpu-tv-l1-optical-flow-with-libjacket","status":"publish","type":"post","link":"https:\/\/mcclanahoochie.com\/blog\/2011\/11\/gpu-tv-l1-optical-flow-with-libjacket\/","title":{"rendered":"GPU TV-L1 Optical Flow with ArrayFire"},"content":{"rendered":"<h3><em>Update 1: LibJacket has been renamed to\u00a0\u00a0<a href=\"http:\/\/www.accelereyes.com\/arrayfire\/\" target=\"_blank\" rel=\"noopener\">ArrayFire<\/a>.<\/em><\/h3>\n<h3><em>Update 2<\/em>: <em>Huang Chao-Hui was nice enough to port the LibJacket code mentioned here to ArrayFire &#8211; see his work <a href=\"http:\/\/huangchtw.blogspot.com\/2012\/02\/tv-l1-based-optical-flow-using-gpu.html\" target=\"_blank\" rel=\"noopener\">here.<\/a><\/em><\/h3>\n<p>As one of my\u00a0<a href=\"http:\/\/web.me.com\/dellaert\/11F-7495\/CS_7495.html\" target=\"_blank\" rel=\"noopener\">Computer Vision<\/a>\u00a0class\u00a0projects, I decided to implement <strong>optical flow,<\/strong> because I wanted to learn more about optical flow, and also I wanted to code it on the GPU (<em>because I love GPU programming<\/em>!).<\/p>\n<p>One of the more successful approaches to optical flow is\u00a0\u00a0<a href=\"http:\/\/en.wikipedia.org\/wiki\/Total_variation\">total variation<\/a>\u00a0L1 regularization (<strong>TV-L1<\/strong>). \u00a0Pock <em>et al<\/em>&#8216;s <span style=\"text-decoration: underline;\"><em>An Improved Algorithm for TV-L1 Optical Flow<\/em><\/span>\u00a0<a href=\"http:\/\/dl.acm.org\/citation.cfm?id=1615232\" target=\"_blank\" rel=\"noopener\">[Paper]<\/a> describes a clever duality based method to solving L1 regularization\u00a0image optical flow. <em>These\u00a0<a style=\"text-decoration: underline;\" href=\"http:\/\/www.werlberger.org\/blog\/files\/talks\/2011-07-12_vm_of_freiburg.pdf\">[slides]<\/a><\/em>\u00a0offer\u00a0a nice intro to variational methods and applications. Kudos to Pock <em>et al<\/em> and their work!<\/p>\n<p>GPU4Vision hosts the reference MATLAB implementation: <a href=\"http:\/\/gpu4vision.icg.tugraz.at\/index.php?content=downloads.php\" target=\"_blank\" rel=\"noopener\">[download]<\/a> of the algorithm in the above paper (bundled in their Matlab Toolbox).\u00a0Having this as a reference, I chose to re-implement this code in LibJacket, because it makes porting MATLAB code into GPU C++ code<em> incredibly easy<\/em>!<\/p>\n<figure id=\"attachment_1897\" aria-describedby=\"caption-attachment-1897\" style=\"width: 574px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png\"><img data-recalc-dims=\"1\" decoding=\"async\" data-attachment-id=\"1897\" data-permalink=\"https:\/\/mcclanahoochie.com\/blog\/2011\/11\/gpu-tv-l1-optical-flow-with-libjacket\/jkt-oflow-tvl1\/#main\" data-orig-file=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png?fit=1680%2C1028&amp;ssl=1\" data-orig-size=\"1680,1028\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"jkt-oflow-tvl1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png?fit=1024%2C626&amp;ssl=1\" class=\"size-large wp-image-1897\" title=\"jkt-oflow-tvl1\" src=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1-1024x626.png?resize=584%2C357\" alt=\"\" width=\"584\" height=\"357\" srcset=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png?resize=1024%2C626&amp;ssl=1 1024w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png?resize=300%2C183&amp;ssl=1 300w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png?resize=490%2C300&amp;ssl=1 490w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-oflow-tvl1.png?w=1680&amp;ssl=1 1680w\" sizes=\"(max-width: 584px) 100vw, 584px\" \/><\/a><figcaption id=\"caption-attachment-1897\" class=\"wp-caption-text\">TV-L1 Optical Flow running on the GPU via LibJacket<\/figcaption><\/figure>\n<p>While the ultimate goal is an OpenCL or CUDA custom\u00a0implementation\u00a0(<span style=\"text-decoration: underline;\">coming here soon!<\/span>), I decided to start with <a href=\"http:\/\/www.accelereyes.com\/\" target=\"_blank\" rel=\"noopener\">AccelerEyes<\/a><strong> LibJacket<\/strong>, because it super easy to translate from MATLAB M scripts to LibJacket C++ GPU code. I made use of what I learned earlier about combining LibJacket and OpenCV (see\u00a0<a href=\"http:\/\/mcclanahoochie.com\/blog\/2011\/08\/image-processing-with-libjacket-opencv\/\" target=\"_blank\" rel=\"noopener\">Image Processing with LibJacket and OpenCV<\/a>) for this project.<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png\"><img data-recalc-dims=\"1\" decoding=\"async\" data-attachment-id=\"1907\" data-permalink=\"https:\/\/mcclanahoochie.com\/blog\/2011\/11\/gpu-tv-l1-optical-flow-with-libjacket\/webcam-tvl1-libjacket\/#main\" data-orig-file=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png?fit=791%2C322&amp;ssl=1\" data-orig-size=\"791,322\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"webcam-tvl1-libjacket\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png?fit=791%2C322&amp;ssl=1\" class=\"aligncenter size-full wp-image-1907\" title=\"webcam-tvl1-libjacket\" src=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png?resize=791%2C322\" alt=\"\" width=\"791\" height=\"322\" srcset=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png?w=791&amp;ssl=1 791w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png?resize=300%2C122&amp;ssl=1 300w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/webcam-tvl1-libjacket.png?resize=500%2C203&amp;ssl=1 500w\" sizes=\"(max-width: 791px) 100vw, 791px\" \/><\/a><\/p>\n<p><strong>Performance<\/strong>: Using the same configuration settings for both my LibJacket version and the MATLAB version, there is <em><span style=\"text-decoration: underline;\">over an order of magnitude improvement<\/span><\/em> in speed, making this fairly complex algorithm capapble of real-time performance.<\/p>\n<p>It&#8217;s <em>still a work in progress<\/em> (a.k.a, probably still bugs), but I&#8217;m glad to share it as open source\u00a0software! [<a title=\"code\" href=\"http:\/\/code.google.com\/p\/mcclanahoochie\/source\/browse\/cuda\/tvl1_libjacket\/optical_flow.cpp\">source code<\/a>] Enjoy!<\/p>\n<p>&nbsp;<\/p>\n<figure id=\"attachment_1902\" aria-describedby=\"caption-attachment-1902\" style=\"width: 574px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png\"><img data-recalc-dims=\"1\" decoding=\"async\" data-attachment-id=\"1903\" data-permalink=\"https:\/\/mcclanahoochie.com\/blog\/2011\/11\/gpu-tv-l1-optical-flow-with-libjacket\/jkt-whale-cv\/#main\" data-orig-file=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png?fit=1175%2C836&amp;ssl=1\" data-orig-size=\"1175,836\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"jkt-whale-cv\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png?fit=1024%2C728&amp;ssl=1\" class=\"aligncenter size-large wp-image-1903\" title=\"jkt-oflow-whale-cv\" src=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv-1024x728.png?resize=584%2C415\" alt=\"\" width=\"584\" height=\"415\" srcset=\"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png?resize=1024%2C728&amp;ssl=1 1024w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png?resize=300%2C213&amp;ssl=1 300w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png?resize=421%2C300&amp;ssl=1 421w, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/11\/jkt-whale-cv.png?w=1175&amp;ssl=1 1175w\" sizes=\"(max-width: 584px) 100vw, 584px\" \/><\/a><figcaption id=\"caption-attachment-1902\" class=\"wp-caption-text\">Middlebury &#8220;rubber whale&#8221; image sequence<\/figcaption><\/figure>\n<p style=\"text-align: center;\">\n","protected":false},"excerpt":{"rendered":"<p>Update 1: LibJacket has been renamed to\u00a0\u00a0ArrayFire. Update 2: Huang Chao-Hui was nice enough to port the LibJacket code mentioned here to ArrayFire &#8211; see his work here. As one of my\u00a0Computer Vision\u00a0class\u00a0projects, I decided to implement optical flow, because I wanted to learn more about optical flow, and also I wanted to code it &#8230; <a title=\"GPU TV-L1 Optical Flow with ArrayFire\" class=\"read-more\" href=\"https:\/\/mcclanahoochie.com\/blog\/2011\/11\/gpu-tv-l1-optical-flow-with-libjacket\/\" aria-label=\"Read more about GPU TV-L1 Optical Flow with ArrayFire\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[91,113,110,103,100,54,114,101,29],"class_list":["post-1896","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-arrayfire","tag-computer-vision","tag-cuda","tag-gpgpu","tag-gpu","tag-image-processing","tag-optical-flow","tag-programming","tag-projects"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pZdXI-uA","jetpack-related-posts":[{"id":1731,"url":"https:\/\/mcclanahoochie.com\/blog\/2011\/08\/image-processing-with-libjacket-opencv\/","url_meta":{"origin":1896,"position":0},"title":"Image processing with LibJacket + OpenCV","author":"mcclanahoochie","date":"August 24, 2011","format":false,"excerpt":"Update: one year later:\u00a0ArrayFire+OpenCV The OpenCV library is the de-facto standard for doing computer vision and image processing research projects. OpenCV includes several hundreds of computer vision algorithms, aimed for use in real-time vision applications. LibJacket is a matrix library built on CUDA. LibJacket offers hundreds of general matrix and\u2026","rel":"","context":"In \"arrayfire\"","block_context":{"text":"arrayfire","link":"https:\/\/mcclanahoochie.com\/blog\/tag\/arrayfire\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/08\/Screen-shot-2011-08-24-at-2.42.52-PM-1024x640.png?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/08\/Screen-shot-2011-08-24-at-2.42.52-PM-1024x640.png?resize=350%2C200 1x, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/08\/Screen-shot-2011-08-24-at-2.42.52-PM-1024x640.png?resize=525%2C300 1.5x"},"classes":[]},{"id":1966,"url":"https:\/\/mcclanahoochie.com\/blog\/2011\/12\/computer-vision-learning-portfolio\/","url_meta":{"origin":1896,"position":1},"title":"Computer Vision Learning Portfolio","author":"mcclanahoochie","date":"December 12, 2011","format":false,"excerpt":"This page constitutes my required\u00a0external\u00a0learning portfolio for CS 7495, Computer Vision, taken in Fall 2011. In it, I discuss what I have learned throughout the course, my activities and findings, how I think I did, and what impact it had on me. About me I am a coffee fanatic that\u2026","rel":"","context":"In \"computer vision\"","block_context":{"text":"computer vision","link":"https:\/\/mcclanahoochie.com\/blog\/tag\/computer-vision\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/12\/chris-raffertys-2-150x150.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1810,"url":"https:\/\/mcclanahoochie.com\/blog\/2011\/09\/opencv-vs-libjacket-gpu-sobel-filtering\/","url_meta":{"origin":1896,"position":2},"title":"OpenCV vs. LibJacket: GPU Sobel Filtering","author":"mcclanahoochie","date":"September 24, 2011","format":false,"excerpt":"Update: LibJacket has been renamed to\u00a0\u00a0ArrayFire. In response to a comment on a previous post about integrating LibJacket into an OpenCV project, below is just a simple FYI performance comparison of OpenCV's GPU Sobel filter versus LibJacket's conv2\u00a0convolution\u00a0filter (with a sobel kernel)... This is an evolutionary post, so be sure\u2026","rel":"","context":"In \"arrayfire\"","block_context":{"text":"arrayfire","link":"https:\/\/mcclanahoochie.com\/blog\/tag\/arrayfire\/"},"img":{"alt_text":"Sobel filter: OpenCV GPU vs. LibJacket","src":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/09\/cv-versus-jkt.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1663,"url":"https:\/\/mcclanahoochie.com\/blog\/2011\/08\/cuda-connected-component-labeling\/","url_meta":{"origin":1896,"position":3},"title":"GPU Connected Component Labeling","author":"mcclanahoochie","date":"August 6, 2011","format":false,"excerpt":"Connected Component Labeling (CCL): \"is used in computer vision to detect connected regions in binary digital images\", and sometimes referred to as blob coloring. Motivation: To keep AccelerEyes'\u00a0ever expanding GPU library growing, over a few weeks of this summer\u00a0I took on the project of writing a CUDA version of connected\u2026","rel":"","context":"In \"arrayfire\"","block_context":{"text":"arrayfire","link":"https:\/\/mcclanahoochie.com\/blog\/tag\/arrayfire\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/08\/coins-bwlabel-300x122.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2347,"url":"https:\/\/mcclanahoochie.com\/blog\/2012\/08\/local-contrast-enhancement-with-arrayfire-opencv\/","url_meta":{"origin":1896,"position":4},"title":"Local Contrast Enhancement with ArrayFire + OpenCV","author":"mcclanahoochie","date":"August 20, 2012","format":false,"excerpt":"About one year ago, I wrote about a simple example of Image Processing with LibJacket + OpenCV... and the trend continues today. In this post, I demonstrate how ArrayFire (an improved version of LibJacket) can easily interop with OpenCV, through a simple example of unsharp maksing (local contrast enhancement). \u00a0\u2026","rel":"","context":"In \"arrayfire\"","block_context":{"text":"arrayfire","link":"https:\/\/mcclanahoochie.com\/blog\/tag\/arrayfire\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2012\/08\/Screen-Shot-2012-08-20-at-9.19.21-AM_2-1024x640.png?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2012\/08\/Screen-Shot-2012-08-20-at-9.19.21-AM_2-1024x640.png?resize=350%2C200 1x, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2012\/08\/Screen-Shot-2012-08-20-at-9.19.21-AM_2-1024x640.png?resize=525%2C300 1.5x"},"classes":[]},{"id":1876,"url":"https:\/\/mcclanahoochie.com\/blog\/2011\/10\/gpu-convolution-opencv-gpu-and-libjacket-part-2\/","url_meta":{"origin":1896,"position":5},"title":"GPU Convolutions: OpenCV GPU and LibJacket &#8211; Part 2","author":"mcclanahoochie","date":"October 24, 2011","format":false,"excerpt":"This is a response to my earlier post comparing OpenCV's gpu::convolve() and LibJacket's jkt::conv2() convolution functions, at various image and kernel sizes. That post generated a lot of traffic, most notably from the OpenCV developer community. Taking note of this, it seems that the folks at Willow Garage have re-vamped\u2026","rel":"","context":"In \"arrayfire\"","block_context":{"text":"arrayfire","link":"https:\/\/mcclanahoochie.com\/blog\/tag\/arrayfire\/"},"img":{"alt_text":".","src":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/10\/Screenshot-f2-2075.png?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/10\/Screenshot-f2-2075.png?resize=350%2C200 1x, https:\/\/i0.wp.com\/mcclanahoochie.com\/blog\/wp-content\/uploads\/2011\/10\/Screenshot-f2-2075.png?resize=525%2C300 1.5x"},"classes":[]}],"jetpack_likes_enabled":false,"_links":{"self":[{"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/posts\/1896","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/comments?post=1896"}],"version-history":[{"count":0,"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/posts\/1896\/revisions"}],"wp:attachment":[{"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/media?parent=1896"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/categories?post=1896"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mcclanahoochie.com\/blog\/wp-json\/wp\/v2\/tags?post=1896"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}