Skip to content

Instantly share code, notes, and snippets.

@Mekajiki
Last active March 1, 2020 16:12
Show Gist options
  • Save Mekajiki/9c5dcd932fd939689c598c0fffb35421 to your computer and use it in GitHub Desktop.
Save Mekajiki/9c5dcd932fd939689c598c0fffb35421 to your computer and use it in GitHub Desktop.
Renderer for text annotation by Google Cloud Vision wrapping on an image
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bs-custom-file-input/dist/bs-custom-file-input.min.js" crossorigin="anonymous"></script>
</head>
<body>
<div class="container-fluid pt-3">
<div id="modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">読み取り結果</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" id="modalBody">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
<input id="apiUrl" type="url" class="form-control" />
</div>
<div class="custom-file">
<form method="POST" id="form">
<input id="imageInput" type="file" name="file" class="custome-file-input" accept="image/x-png,image/gif,image/jpeg,image/tiff" />
<label class="custom-file-label" for="imageInput">Choose source image file</label>
</form>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="accessApi" id="aniposApi" value="aniposApi" checked/>
<label class="form-check-label" for="aniposApi">
ANIPOS API
</label>
</div>
</div>
<div id="fileSelector" class="col">
<div class="custom-file">
<input id="recognitionInput" type="file" class="custome-file-input" accept="text/json" />
<label class="custom-file-label" for="recognitionInput">Choose recognition result json</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="sourceEntity" id="textAnnotations" value="textAnnotations" >
<label class="form-check-label" for="textAnnotations">
textAnnotations
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="sourceEntity" id="fullTextAnnotation" value="symbols" checked>
<label class="form-check-label" for="fullTextAnnotation">
fullTextAnnotation
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="hierarchy" id="hierarchy" value="hierarchy">
<label class="form-check-label" for="hierarchy">
hierarchy
</label>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div id="canvasWrapper" style="position: relative">
<canvas id="canvas" style="position: relative"></canvas>
</div>
</div>
</div>
</div>
</body>
<script>
bsCustomFileInput.init()
var json = null
function drawImage(canvas, imageFile) {
var ctx = canvas.getContext('2d'),
img = new Image(),
url = window.URL || window.webkitURL,
src = url.createObjectURL(imageFile)
img.src = src
img.onload = function() {
canvas.width = this.width
canvas.height = this.height
ctx.canvas.width = this.width
ctx.canvas.height = this.height
ctx.drawImage(img, 0, 0)
url.revokeObjectURL(src)
}
}
function annotate(vertices, title, color) {
var pos = vertices[0]
var farSide = vertices[2]
appendAnnotation(pos, farSide, title, color)
}
function appendAnnotation(pos, farSide, title, color) {
var size = {x: farSide.x - pos.x, y: farSide.y - pos.y}
var element = document.createElement("div")
title += "<br>" +
"x: "+ pos.x + ", y: " + pos.y + "<br>" +
"width: "+ size.x + ", height: " + size.y + "<br>"
var style = element.style
style.position = 'absolute'
style.top = pos.y + "px"
style.left = pos.x + "px"
style.width = size.x + "px"
style.height = size.y + "px"
style.border = 'solid 2px ' + color
element.setAttribute('data-toggle', 'tooltip')
element.setAttribute('data-html', true)
element.setAttribute('data-placement', 'top')
element.setAttribute('title', title)
element.classList.add('annotation')
$(element).tooltip()
parent().appendChild(element)
}
function drawToolTips(recognition) {
recognition.responses.forEach(function(response) {
if (document.getElementById("textAnnotations").checked) {
response.text_annotations.slice(1).forEach(function(annotation, index) {
var title = "index: " + index + "<br>" +
"content: " + annotation.description
annotate(annotation.bounding_poly.vertices, title, "red")
})
}
else {
index = 0
var hierarchy = document.getElementById("hierarchy").checked
response.full_text_annotation.pages.forEach(function(page){
page.blocks.forEach(function(block){
if (hierarchy) { annotate(block.bounding_box.vertices, "block", "navy")}
block.paragraphs.forEach(function(paragraph){
if (hierarchy) { annotate(paragraph.bounding_box.vertices, "paragraph", "teal") }
paragraph.words.forEach(function(word){
if (hierarchy) { annotate(word.bounding_box.vertices, "word", "orange") }
word.symbols.forEach(function(symbol){
var pos = symbol.bounding_box.vertices[0]
var farSide = symbol.bounding_box.vertices[2]
var size = {x: farSide.x - pos.x, y: farSide.y - pos.y}
var title = index + "<br>" + symbol.text
if (symbol.property && symbol.property.detected_break) {
title += "<br>break: " + symbol.property.detected_break.type + ", " + symbol.property.detected_break.is_prefix
}
else {
console.log("no property")
console.log(symbol)
}
annotate(symbol.bounding_box.vertices, title, "red")
++index
})
})
})
})
})
}
})
}
function removeAllToolTips() {
Array.from(parent().getElementsByClassName('annotation')).forEach(function(element){
parent().removeChild(element)
})
}
function drawAnipos(res) {
var receipt = res.receipt
removeAllToolTips()
var summary = "<strong>合計金額</strong>: " + receipt.total_price + "円<br>" +
"<strong>項目</strong><br>"
receipt.line_items.forEach(function(item) { summary += " " + item.name + ":" + item.price + "円<br>"})
document.getElementById("modalBody").innerHTML = summary
$("#modal").modal()
annotate(receipt.total_block.rect, receipt.total_block.string, "red")
receipt.line_items.forEach(function(lineItem){
annotate(lineItem.line.rect, "", "blue")
lineItem.line.blocks.forEach(function(block) {
annotate(block.rect, block.string, "green")
})
})
}
function parent(){
return document.getElementById('canvasWrapper')
}
function redrawToolTips() {
if (json) {
removeAllToolTips()
drawToolTips(json)
}
}
function sourceEntityChanged(ev) {
redrawToolTips()
}
function onRecognitionLoad(ev) {
json = JSON.parse(ev.target.result)
redrawToolTips()
}
function recognitionOnChange(ev) {
var reader = new FileReader()
reader.onload = onRecognitionLoad
reader.readAsText(ev.target.files[0])
}
function imageOnChange(ev) {
drawImage(document.getElementById('canvas'), ev.target.files[0])
if (document.getElementById('aniposApi').checked) {
var form = document.getElementById('form')
var data = new FormData(form);
var url = document.getElementById('apiUrl').value
var xhr = new XMLHttpRequest()
xhr.onload = function() {
if (xhr.status === 200) {
console.log(xhr.responseText)
res = JSON.parse(xhr.responseText)
drawAnipos(res)
}
else {
}
};
xhr.open("POST", url);
xhr.send(data);
}
}
function aniposApiOnChange(ev) {
$('#fileSelector input').prop('disabled', document.getElementById("aniposApi").checked)
}
document.getElementById("imageInput").addEventListener("change", imageOnChange, true)
document.getElementById("recognitionInput").addEventListener("change", recognitionOnChange, true)
document.getElementById("fullTextAnnotation").addEventListener("change", sourceEntityChanged, true)
document.getElementById("hierarchy").addEventListener("change", redrawToolTips, true)
document.getElementById("aniposApi").addEventListener("change", aniposApiOnChange, true)
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment