Last active
March 1, 2020 16:12
-
-
Save Mekajiki/9c5dcd932fd939689c598c0fffb35421 to your computer and use it in GitHub Desktop.
Renderer for text annotation by Google Cloud Vision wrapping on an image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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">×</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