From 46ed331d0438c6af704ea2493950490208b746c0 Mon Sep 17 00:00:00 2001 From: Usagi Ito Date: Sun, 8 Oct 2023 11:48:09 +0900 Subject: [PATCH] fix #1 (#29) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /intput 画像認識 カラースポイトで文字色を抽出して前処理する機能 --- input/index.html | 15 ++++---- resources/css/input.css | 23 ++++++++++++ resources/js/common/image.js | 24 +++++++++++-- resources/js/input.image.js | 69 ++++++++++++++++++++++++++++-------- 4 files changed, 109 insertions(+), 22 deletions(-) diff --git a/input/index.html b/input/index.html index c27c90e..babc4a1 100644 --- a/input/index.html +++ b/input/index.html @@ -15,7 +15,8 @@
- +
@@ -43,14 +44,16 @@ 🔗lang code - +
+ +
+ +
- +
diff --git a/resources/css/input.css b/resources/css/input.css index a84db20..6be3b88 100644 --- a/resources/css/input.css +++ b/resources/css/input.css @@ -149,6 +149,29 @@ div.image label:has(.lang)::before { content: "認識言語"; } +div.image label:has(.color-filter)::after { + content: "色フィルター"; +} + +div.image label:has(.tolerance)::before { + content: "±"; + margin: 0 0.2em; +} + +div.image .color { + width: 2em; + height: 1em; + border: 0.1em solid gray; + background: #fff; + display: inline-block; + vertical-align: sub; +} + +div.image .tolerance { + width: 3em; + text-align: center; +} + div.image input.lang { width: 4em; text-align: center; diff --git a/resources/js/common/image.js b/resources/js/common/image.js index 6313a68..37988b8 100644 --- a/resources/js/common/image.js +++ b/resources/js/common/image.js @@ -17,13 +17,34 @@ export function trim_colors(canvas, colors) ctx.putImageData(image_data, 0, 0); } +export function pick_color(img, click_event) +{ + let click_x = click_event.clientX - img.getBoundingClientRect().left + let click_y = click_event.clientY - img.getBoundingClientRect().top + + let pixel_x = Math.floor((click_x / img.clientWidth) * img.naturalWidth) + let pixel_y = Math.floor((click_y / img.clientHeight) * img.naturalHeight) + + let canvas = document.createElement('canvas') + canvas.width = img.naturalWidth + canvas.height = img.naturalHeight + + let ctx = canvas.getContext('2d') + ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight) + + window.c = canvas + let data = ctx.getImageData(pixel_x, pixel_y, 1, 1).data + + return [...data] +} + // img を編集する: // - 効果1: colors で与えられた色のピクセルだけを残して、他のピクセルを透明にする // - 効果2: colors で与えられた色のピクセルの範囲を検出して、その範囲でクロップする // args: // img : // colors: [[r,g,b,d], ...]] -export function extract_colors(img, colors = [[255, 255, 255]]) +export function extract_colors(img, colors = [[255, 255, 255, 144]]) { let canvas = document.createElement('canvas') canvas.width = img.naturalWidth @@ -47,7 +68,6 @@ export function extract_colors(img, colors = [[255, 255, 255]]) Math.abs(data[data_index] - color[0]) <= color[3] && Math.abs(data[data_index + 1] - color[1]) <= color[3] && Math.abs(data[data_index + 2] - color[2]) <= color[3] - // && data[data_index + 3] === 255 // 上から下へ検索 for (let y = 0; y < height; y++) diff --git a/resources/js/input.image.js b/resources/js/input.image.js index 0a7e54d..8c9910e 100644 --- a/resources/js/input.image.js +++ b/resources/js/input.image.js @@ -1,12 +1,9 @@ import VacApi from './api.js' import { ISO639_ALPHA3 } from './common/lang_code.js' import { unorm_to_percent } from './common/number.js' -import { extract_colors } from './common/image.js' +import { extract_colors, pick_color } from './common/image.js' -// v: value of color element, d: delta of color element -let c = (v, d) => [v, v, v, d] - -export const ARKNIGHTS_SENARIO_TEXT = { colors: [c(255, 2), c(146, 2)], min_length: 2, confidence_filter: 80 } +const DEFAULT_FILTER_COLOR = [255, 255, 255, 144] export default class VacInputImage { @@ -22,6 +19,9 @@ export default class VacInputImage { this.elements = {} this.elements.img = root_element.querySelector('img') + this.elements.color_filter = root_element.querySelector('.image .color-filter') + this.elements.color = root_element.querySelector('.image .color') + this.elements.tolerance = root_element.querySelector('.image .tolerance') this.elements.auto_input = root_element.querySelector('.image .auto-input') this.elements.continuous = root_element.querySelector('.image .continuous') this.elements.one_shot = root_element.querySelector('.image .one-shot') @@ -39,6 +39,11 @@ export default class VacInputImage this.elements.lang.addEventListener('change', () => this.lang_changed()) this.elements.lang.addEventListener('keydown', () => this.lang_changed()) + this.elements.img.addEventListener('click', e => this.pick_color(e)) + this.elements.color_filter.addEventListener('change', () => this.save()) + this.elements.color.addEventListener('click', () => this.activate_pick_color()) + this.elements.tolerance.addEventListener('change', () => this.input_tolerance()) + // paste したら画像を読み込み document.onpaste = e => { @@ -83,8 +88,6 @@ export default class VacInputImage this.image_recognizer() } }) - - this.elements.preprocessor.addEventListener('input', () => this.save()) } lang_changed() @@ -107,7 +110,9 @@ export default class VacInputImage continuous: this.elements.continuous.checked, lang: this.elements.lang.value, - preprocessor: this.elements.preprocessor.value + is_enabled_color_filter: this.elements.color_filter.checked, + + filter_color: this.filter_color, })) } @@ -122,8 +127,11 @@ export default class VacInputImage this.elements.continuous.checked = s.continuous this.elements.lang.value = s.lang - if (s.preprocessor) - this.elements.preprocessor.value = s.preprocessor + this.elements.color_filter.checked = s.is_enabled_color_filter || false + + this.filter_color = s.filter_color || DEFAULT_FILTER_COLOR + this.elements.color.style.backgroundColor = `rgb(${this.filter_color[0]},${this.filter_color[1]},${this.filter_color[2]})` + this.elements.tolerance.value = this.filter_color[3] } } @@ -139,10 +147,8 @@ export default class VacInputImage let l = this.elements.lang.value // pre process - switch (this.elements.preprocessor.value.toLowerCase()) - { - case 'arknights-story-text': extract_colors(this.elements.img, ARKNIGHTS_SENARIO_TEXT.colors); break - } + if (this.elements.color_filter.checked) + extract_colors(this.elements.img, [this.filter_color]) // recognize let r = await Tesseract.recognize( @@ -198,6 +204,41 @@ export default class VacInputImage reader.readAsDataURL(file) } + + + filter_color = DEFAULT_FILTER_COLOR + pick_color = e => + { + if (!this.is_activated_pick_color) + return + + this.is_activated_pick_color = false + this.elements.img.style.objectFit = '' + this.elements.img.style.cursor = '' + this.elements.color.style.cursor = '' + + let color = pick_color(this.elements.img, e) + this.filter_color = [...color.slice(0, 3), this.filter_color[3]] + this.elements.color.style.backgroundColor = `rgb(${color[0]},${color[1]},${color[2]})` + + this.save() + } + + is_activated_pick_color = false + activate_pick_color = () => + { + this.is_activated_pick_color = true + this.elements.img.style.objectFit = 'fill' + this.elements.img.style.cursor = 'crosshair' + this.elements.color.style.cursor = 'crosshair' + } + + input_tolerance = () => + { + this.filter_color[3] = this.elements.tolerance.value + this.save() + } + } window.VacInputImage = VacInputImage