<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>OCR on SNow adventures</title>
    <link>https://www.snow-adventures.com/tags/ocr/</link>
    <description>Recent content in OCR on SNow adventures</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <copyright>© Samuel Meylan - 2020</copyright>
    <lastBuildDate>Sun, 20 Dec 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://www.snow-adventures.com/tags/ocr/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Search on screenshot attachments</title>
      <link>https://www.snow-adventures.com/posts/search-on-screenshot-attachments/</link>
      <pubDate>Sun, 20 Dec 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.snow-adventures.com/posts/search-on-screenshot-attachments/</guid>
      <description>OCR on picture attachments with Google vision API</description>
      <content:encoded><![CDATA[<h2 id="ocr-on-attachment-with-google-vision-api">OCR on attachment with Google vision API</h2>
<p>This article shows how to use the Google Vision API to extract text from attachments and to make this text searchable in Service-Now.</p>
<p>In this quick demo, we can see that attaching printscreens to an incident will trigger a call to the Google Vision API and extract the text in a field <em>Extracted field</em> on the attachment table and consolidate all the attachment texts in a field <em>Attachment text</em> on the incident.</p>
<figure>
    <img loading="lazy" src="/img/Search_on_screenshot/Attachment%20OCR%20demo.gif"/> <figcaption>
            Adding attachment triggers OCR and extract text
        </figcaption>
</figure>

<p>Then we can search string that are present only on the screenshots, like &ldquo;Wikipedia&rdquo; or &ldquo;Croissant&rdquo; in this case.</p>
<figure>
    <img loading="lazy" src="/img/Search_on_screenshot/Attachment%20OCR%20demo%20search.gif"/> <figcaption>
            Search with text from the screenshots
        </figcaption>
</figure>

<p>Now, let&rsquo;s see how to implement this solution.</p>
<h2 id="setup-the-google-api">Setup the google APi</h2>
<p>First of all, you will need a Google Cloud account.</p>
<p>If you don&rsquo;t already have a Google Cloud account, you can setup your account here: <a href="https://cloud.google.com/">https://cloud.google.com/</a></p>
<p>When done, go in the Google Cloud console and follow the instruction in the starting guide[1] and in the Detect text in images guide[2].</p>
<p>Basically you will need to:</p>
<ul>
<li>create a new Project</li>
<li><a href="https://console.cloud.google.com/flows/enableapi?apiid=vision.googleapis.com">activate the Vision API</a> for your project</li>
<li>Create an API key in the credential
<ul>
<li>Go in the menu, the &ldquo;API &amp; Services&rdquo; - &ldquo;Credentials&rdquo;
<figure>
	    <img loading="lazy" src="/img/Search_on_screenshot/63d7441b41fac25810743cf4c7ba4f73.png"/> <figcaption>
	            Create API credentials
	        </figcaption>
	</figure>
</li>
<li>Then click &ldquo;Create Credential&rdquo; - &ldquo;API key&rdquo;
<figure>
	    <img loading="lazy" src="/img/Search_on_screenshot/5c09b2c362592fd867aaf05805e5af55.png"/> <figcaption>
	            Create API credentials
	        </figcaption>
	</figure>
</li>
<li>Then copy and keep the key for later reference.</li>
</ul>
</li>
</ul>
<p>You should now be ready to use the google API.</p>
<blockquote>
<p>Obviously, for real life usage, you may want to use other authentification methods and/or add some more security. To do so please refer to the Google Cloud documentation or consult a Google Cloud specialist.</p>
</blockquote>
<h2 id="adapt-the-attachment-table">Adapt the attachment table</h2>
<p>The idea is to store the extracted text from the image in a text field on the attachment table.</p>
<p>Before doing so, I suggest to work in an Scoped Application: so please create and use a new Application &ldquo;OCR Attachment&rdquo;.</p>
<p>Then, let&rsquo;s create a new field <code>u_extracted_text</code> on the table <code>sys_attachment</code>, of type String and of a sufficient size of 4000.</p>
<p>Then we need give access at our application &ldquo;OCR Attachment&rdquo;  to the Attachment table:</p>
<ul>
<li>go in &ldquo;System Definition&rdquo; - &ldquo;Tables&rdquo;</li>
<li>select the sys_attachment table</li>
<li>on the &ldquo;Application Access&rdquo; tab set &ldquo;Can Update&rdquo; to true. <figure>
    <img loading="lazy" src="/img/Search_on_screenshot/bd99ff397e2a5e1ed9200b8d8c00e40c.png"/> <figcaption>
            Create API credentials
        </figcaption>
</figure>
</li>
<li>To avoid potential issues later, please also reset the cache by doing a <code>cache.do</code> in the Filter Navigator.</li>
</ul>
<h2 id="create-the-rest-message">Create the REST message</h2>
<p>Now we need to create a REST message and the corresponding HTTP method.</p>
<p>Simply create a new REST message &ldquo;Google Vision API&rdquo; and set the endpoint to</p>
<pre tabindex="0"><code class="language-url" data-lang="url">https://vision.googleapis.com/v1/images:annotate
</code></pre><p>Then open the prefefined HTTP method that is automatically created when saving the REST message and</p>
<ul>
<li>renamne it to &ldquo;POST&rdquo;</li>
<li>Set the HTTP method to &ldquo;POST&rdquo;</li>
<li>Set the endpoint to <code>https://vision.googleapis.com/v1/images:annotate?key=${apikey}</code></li>
<li>On the &ldquo;HTTP request&rdquo; tab, set the Content to
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;requests&#34;</span><span class="o">:</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">	  <span class="p">{</span>
</span></span><span class="line"><span class="cl">		 <span class="s2">&#34;image&#34;</span><span class="o">:</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="s2">&#34;content&#34;</span><span class="o">:</span><span class="s2">&#34;${base64Image}&#34;</span>
</span></span><span class="line"><span class="cl">		 <span class="p">},</span>
</span></span><span class="line"><span class="cl">		 <span class="s2">&#34;features&#34;</span><span class="o">:</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">			<span class="p">{</span>
</span></span><span class="line"><span class="cl">			   <span class="s2">&#34;type&#34;</span><span class="o">:</span><span class="s2">&#34;TEXT_DETECTION&#34;</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">		 <span class="p">]</span>
</span></span><span class="line"><span class="cl">	  <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<p>The important points are the variable names <code>apikey</code> and <code>base64Image</code>, that we will use in the Business Rule later.</p>
<h3 id="testing-the-rest-message">Testing the rest message</h3>
<p>To test you REST message and HTTP request, you can add 2 entries in the Variable Substitution related list:</p>
<ul>
<li>apikey: containing the API you created before in the Google Cloud console</li>
<li>base64Image: containing a base64 encoded image that have text in it.
<ul>
<li>You can use this <a href="https://www.base64-image.de/">online base64 converter</a> to convert your image.</li>
</ul>
</li>
</ul>
<p>Then you can use the <em>test</em> related link and you should get a code 200 and a json string with the text extracted from your image.</p>
<h2 id="create-a-business-rule-on-the-attachment-table">Create a Business Rule on the attachment table</h2>
<p>Now that both the table and the REST message are ready, we can create a new Business Rule:</p>
<ul>
<li>Name : &ldquo;Extract text from image&rdquo;</li>
<li>Table: sys_attachment</li>
<li>Advanced: true</li>
<li>When to Run
<ul>
<li>When: async</li>
<li>On insert</li>
<li>Condition: Extracted Text is Empty and File name ends with .jpg, .jpeg or .png (adapt the list to your taste)</li>
</ul>
</li>
</ul>
<p>The in the <em>Advanced</em> tab, you can use and adapt the following code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span> <span class="nx">executeRule</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">previous</span> <span class="cm">/*null when async*/</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// get base64 string from file 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="kd">var</span> <span class="nx">base64Str</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideSysAttachment</span><span class="p">().</span><span class="nx">getContentBase64</span><span class="p">(</span><span class="nx">current</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="c1">// get API key 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="kd">var</span> <span class="nx">apiKey</span> <span class="o">=</span> <span class="nx">gs</span><span class="p">.</span><span class="nx">getProperty</span><span class="p">(</span><span class="s1">&#39;x_12270_ocr_attach.googleVision_api_key&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="nx">base64Str</span> <span class="o">&amp;&amp;</span> <span class="nx">apiKey</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// build request
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="kd">var</span> <span class="nx">request</span><span class="o">=</span> <span class="k">new</span> <span class="nx">sn_ws</span><span class="p">.</span><span class="nx">RESTMessageV2</span><span class="p">(</span><span class="s1">&#39;x_12270_ocr_attach.Google Vision API&#39;</span><span class="p">,</span> <span class="s1">&#39;POST&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="nx">request</span><span class="p">.</span><span class="nx">setStringParameterNoEscape</span><span class="p">(</span><span class="s1">&#39;base64Image&#39;</span><span class="p">,</span> <span class="nx">base64Str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="nx">request</span><span class="p">.</span><span class="nx">setStringParameterNoEscape</span><span class="p">(</span><span class="s1">&#39;apikey&#39;</span><span class="p">,</span> <span class="nx">apiKey</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="c1">// execute request
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="kd">var</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">execute</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="c1">// extract response
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="kd">var</span> <span class="nx">responseBody</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">getBody</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">			<span class="kd">var</span> <span class="nx">httpStatus</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">getStatusCode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// if success
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="k">if</span> <span class="p">(</span><span class="nx">httpStatus</span> <span class="o">==</span> <span class="s1">&#39;200&#39;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">				<span class="nx">responseObj</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">responseBody</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">				<span class="k">if</span> <span class="p">(</span><span class="nx">responseObj</span> <span class="o">&amp;&amp;</span> <span class="nx">responseObj</span><span class="p">.</span><span class="nx">responses</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="nx">responseObj</span><span class="p">.</span><span class="nx">responses</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">fullTextAnnotation</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">					<span class="kd">var</span> <span class="nx">text</span> <span class="o">=</span> <span class="nx">responseObj</span><span class="p">.</span><span class="nx">responses</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">fullTextAnnotation</span><span class="p">.</span><span class="nx">text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">					<span class="k">if</span> <span class="p">(</span><span class="nx">text</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">						<span class="nx">current</span><span class="p">.</span><span class="nx">u_extracted_text</span> <span class="o">=</span> <span class="nx">text</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">						<span class="nx">current</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">					<span class="p">}</span>
</span></span><span class="line"><span class="cl">				<span class="p">}</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">exception</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">		<span class="nx">gs</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error in Business rule &#39;Extract text from image&#39;: &#34;</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">exception</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">})(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">previous</span><span class="p">);</span>
</span></span></code></pre></div><p>Then the last step is to create a new system property <code>googleVision_api_key</code> and set there the API key you created from the Google Cloud Console.</p>
<h2 id="searching-tasks-using-attachments-text">Searching tasks using attachment&rsquo;s text</h2>
<p>Now the last part is to be able to search records (like incidents, etc.. ) where the related attachment contains the searched text.</p>
<p>The easiest way for this is to create a new field on the <em>Task</em> table that will contains the extracted text of all the attachments. This way the text contained in this field (the text extracted from the attachments) will be included in the Zing search on tasks.</p>
<p>So, let&rsquo;s create a new field <code>attachment_text</code> on the table <code>task</code>, of type String and of a sufficient size of 4000.</p>
<p>Then we need a new Business Rule on the table <code>sys_attachment</code>:</p>
<ul>
<li>Name : &ldquo;Maintain extracted text on related task&rdquo;</li>
<li>Table: sys_attachment</li>
<li>Advanced: true</li>
<li>When to Run
<ul>
<li>When: after</li>
<li>On insert, update, delete</li>
<li>Condition: Extracted Text changes</li>
</ul>
</li>
<li>Conditions:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">GlideTableHierarchy</span><span class="p">(</span><span class="nx">current</span><span class="p">.</span><span class="nx">table_name</span><span class="p">).</span><span class="nx">getRoot</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&#34;task&#34;</span> <span class="cm">/*check that the related table is child of task*/</span>
</span></span></code></pre></div><p>The in the <em>Advanced</em> tab, add the following code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span> <span class="nx">executeRule</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">previous</span> <span class="cm">/*null when async*/</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">try</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// get the related table
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="kd">var</span> <span class="nx">taskGr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideRecord</span><span class="p">(</span><span class="nx">current</span><span class="p">.</span><span class="nx">table_name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="nx">taskGr</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">current</span><span class="p">.</span><span class="nx">table_sys_id</span><span class="p">)){</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// to hold the attachment texts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="kd">var</span> <span class="nx">attachmentTextObj</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// make sure the attachment text field is valid
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="k">if</span> <span class="p">(</span><span class="nx">taskGr</span><span class="p">.</span><span class="nx">isValidField</span><span class="p">(</span><span class="s1">&#39;x_12270_ocr_attach_attachment_text&#39;</span><span class="p">)){</span> <span class="cm">/*ADAPT according you Application mame, etc...*/</span>
</span></span><span class="line"><span class="cl">				<span class="c1">// if the attachment text field exist and is not empty, parse it
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>				<span class="k">if</span> <span class="p">(</span><span class="nx">taskGr</span><span class="p">.</span><span class="nx">x_12270_ocr_attach_attachment_text</span><span class="p">){</span> <span class="cm">/*ADAPT according you Application mame, etc...*/</span>
</span></span><span class="line"><span class="cl">					<span class="nx">attachmentTextObj</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">taskGr</span><span class="p">.</span><span class="nx">x_12270_ocr_attach_attachment_text</span><span class="p">);</span> <span class="cm">/*ADAPT according you Application mame, etc...*/</span>
</span></span><span class="line"><span class="cl">				<span class="p">}</span>
</span></span><span class="line"><span class="cl">				<span class="c1">// if we delete the attachment, remove the corresponding text
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>				<span class="k">if</span> <span class="p">(</span><span class="nx">current</span><span class="p">.</span><span class="nx">operation</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;delete&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">					<span class="k">delete</span> <span class="nx">attachmentTextObj</span><span class="p">[</span><span class="nx">current</span><span class="p">.</span><span class="nx">getUniqueValue</span><span class="p">()];</span>
</span></span><span class="line"><span class="cl">				<span class="p">}</span> <span class="k">else</span> <span class="cm">/*otherwise, set the text*/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">					<span class="nx">attachmentTextObj</span><span class="p">[</span><span class="nx">current</span><span class="p">.</span><span class="nx">getUniqueValue</span><span class="p">()]</span> <span class="o">=</span> <span class="nx">current</span><span class="p">.</span><span class="nx">u_extracted_text</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">				<span class="p">}</span>
</span></span><span class="line"><span class="cl">				<span class="c1">// Set back the attachment text to string and update
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>				<span class="nx">taskGr</span><span class="p">.</span><span class="nx">x_12270_ocr_attach_attachment_text</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">attachmentTextObj</span><span class="p">);</span> <span class="cm">/*ADAPT according you Application mame, etc...*/</span>
</span></span><span class="line"><span class="cl">				<span class="nx">taskGr</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">exception</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">		<span class="nx">gs</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error in Business rule &#39;Maintain extracted text on related task&#39;: &#34;</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">exception</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">})(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">previous</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>You can now take a print screen with text in it and attach it to a record like an <em>Incident</em>:</p>
<ul>
<li>The <code>sys_attachment</code> record now have the text extracted in to the <code>Extracted text</code>field.</li>
<li>The <code>incident record</code> record now have also the text in the  <code>attachment_text</code> field.</li>
</ul>
<p>You can now search for a String that is present on the screenshot and you incident should show up in the search result</p>
<h2 id="references">References</h2>
<ol>
<li>Cloud Vision API - Documentation - Quickstart: Setup the Vision API <a href="https://cloud.google.com/vision/docs/setup">https://cloud.google.com/vision/docs/setup</a></li>
<li>Cloud Vision API - Documentation - Detect text in images <a href="https://cloud.google.com/vision/docs/ocr">https://cloud.google.com/vision/docs/ocr</a></li>
</ol>]]></content:encoded>
    </item>
    
  </channel>
</rss>
