{"id":436,"date":"2023-03-20T12:14:30","date_gmt":"2023-03-20T17:14:30","guid":{"rendered":"https:\/\/davidwdrell.net\/wordpress\/?p=436"},"modified":"2023-05-22T06:05:15","modified_gmt":"2023-05-22T11:05:15","slug":"save-framos-imx-253-images-as-jpeg-on-nvidia-jetson","status":"publish","type":"post","link":"https:\/\/davidwdrell.net\/wordpress\/?p=436","title":{"rendered":"Save Framos IMX 253 images as Jpeg on NVIDIA Jetson"},"content":{"rendered":"\n<p>I was recently working on a demonstration of a Framos IMX 253 mono-chrome camera with a 12-bit sensor supported on a Jetson Xavier.   For the demo, I needed to save the images as jpeg format.  I thought it may be useful for others to see the inner-workings of a jpeg compression implementation Jetson, using the hardware assisted jpeg compressor.<\/p>\n\n\n\n<p>The image came from the camera driver as 12 bit format in 16 bit integer array format. The sensor is mono-chrome, but the Jetson jpeg compressor only takes a single YUV format as input.<\/p>\n\n\n\n<p>For this demo, I skip the step of re-mapping the luminance values from a 12 bit range to an 8 bit range and  simply take the lower 8-bits. A production implementation needs a scheme for this re-mapping of luminance range. The implementation on a Jetson should take advantage of the hardware assist.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Step 1: get things setup<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ Info is a structure provided from the caller that contains info about the frame \n\n\/\/ prepare to time execution of the jpeg compression    \nauto start = std::chrono::steady_clock::now();\n\n\/\/ prepare the output file\nstd::string outFile=\"\/path\/to\/file.jpg\";\nstd::ofstream* outFileStr = new std::ofstream(outFile);\nif(!outFileStr-&gt;is_open())\n        return false;\n\n\/\/ create an instance of the nvidia jetson jpeg encoder\n\nNvJPEGEncoder* jpegenc = NvJPEGEncoder::createJPEGEncoder(\"jpenenc\");\n\n\/\/ the jpeg output buffer size is 1.5 times the width*height \nunsigned long out_buf_size = Info.Width * Info.Height * 3 \/ 2;\nunsigned char *out_buf = new unsigned char&#91;out_buf_size];\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Step 2: create an nvidia native buffer<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ V4L2_PIX_FMT_YUV420M =  is the only format which appears to be supported by the Jetson jpeg encoder\n\n\/\/ allocate the buffer    \nNvBuffer buffer(V4L2_PIX_FMT_YUV420M, Info.Width, Info.Height , 0);\n\nbuffer.allocateMemory();\n\nNvBuffer::NvBufferPlane* plane = &amp;buffer.planes&#91;0];\n\n\/\/convert the image luminance from uint16 to 8 bits and copy into the nvidia buffer\nfor(int y=0; y &lt; Info.Height;y++)\n{\n    for(int x=0; x &lt; Info.Width;x++)\n    {\n        plane-&gt;data&#91;x+(y*plane-&gt;fmt.stride)] = (unsigned char) (m_img&#91;x+(y*Info.Width)]);\n    }\n}\n\nplane-&gt;bytesused = 1 * plane-&gt;fmt.stride * plane-&gt;fmt.height;\n<\/code><\/pre>\n\n\n\n<p>Step 3: the Framos camera driver provides a mono-chrome image, so make the image actually mono-chrome by setting the UV vectors to neutral color (127d). <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> \/\/ initialize the Cb plan to the 127d value which means 0 color\n\nplane = &amp;buffer.planes&#91;1];\nchar* data = (char *) plane-&gt;data;\nplane-&gt;bytesused = 0;\nfor (int j = 0; j &lt; plane-&gt;fmt.height; j++)\n{\n    memset(data,127,plane-&gt;fmt.width);\n    data += plane-&gt;fmt.stride;\n}\nplane-&gt;bytesused = plane-&gt;fmt.stride * plane-&gt;fmt.height;\n\n\/\/ initialize the Cr plan to the 127d value which means 0 color\n\nplane = &amp;buffer.planes&#91;2];\ndata = (char *) plane-&gt;data;\nplane-&gt;bytesused = 0;\nfor (int j = 0; j &lt; plane-&gt;fmt.height; j++)\n{\n    memset(data,127,plane-&gt;fmt.width);\n    data += plane-&gt;fmt.stride;\n}\nplane-&gt;bytesused = plane-&gt;fmt.stride * plane-&gt;fmt.height;<\/code><\/pre>\n\n\n\n<p>Step 4: run the actual jpeg encode function, save the file, and measure the results:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jpegenc-&gt;encodeFromBuffer(buffer, JCS_YCbCr, &amp;out_buf, out_buf_size, 95);\n\nauto end = std::chrono::steady_clock::now();\n\noutFileStr-&gt;write((char *) out_buf, out_buf_size);\noutFileStr-&gt;close();\n\nprintf( \"Jpeg Encode Elapsed time in nanoseconds: %d\\n\",std::chrono::duration_cast&lt;std::chrono::nanoseconds&gt;(end - start).count());\n\ndelete&#91;] out_buf;\ndelete outFileStr;\n<\/code><\/pre>\n\n\n\n<p>Result:<\/p>\n\n\n\n<p>I am seeing roughly 25 milliseconds  for the encode and save on an image 3840 x 2160 pixels.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was recently working on a demonstration of a Framos IMX 253 mono-chrome camera with a 12-bit sensor supported on [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[9],"tags":[],"class_list":["post-436","post","type-post","status-publish","format-standard","hentry","category-nvidia-jetson"],"_links":{"self":[{"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=436"}],"version-history":[{"count":9,"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/436\/revisions"}],"predecessor-version":[{"id":448,"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/436\/revisions\/448"}],"wp:attachment":[{"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davidwdrell.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}