feat(ocr): T0a/T0/T1 — golden harness(CTC blank=0 도출) + deps(ort rc.9) + dict/NOTICE
T0a: onnxruntime 직접 골든 하네스 → CTC blank/dict 매핑 경험 확정(gt CER 0.000). T0: 모델 번들 dict+NOTICE(.onnx 는 T12 LFS 결정까지 워크트리 보관). T1: ort(download-binaries)+imageproc 추가, cargo tree ort rc.9 단일 확인.
This commit is contained in:
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# PP-OCRv5 ONNX OCR models (paddle-onnx engine) — large binary, store via Git LFS
|
||||
# to keep clone / `cargo package` lean. Enable with `git lfs install` before commit.
|
||||
*.onnx filter=lfs diff=lfs merge=lfs -text
|
||||
78
Cargo.lock
generated
78
Cargo.lock
generated
@@ -4417,6 +4417,24 @@ dependencies = [
|
||||
"quick-error 2.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imageproc"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "602b4e8a4cc3e98372b766cd184ab532999bc0e839b7469e759511ccabc65d77"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"approx",
|
||||
"getrandom 0.2.17",
|
||||
"image",
|
||||
"itertools 0.12.1",
|
||||
"nalgebra",
|
||||
"num",
|
||||
"rand 0.8.6",
|
||||
"rand_distr 0.4.3",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgref"
|
||||
version = "1.12.1"
|
||||
@@ -4548,6 +4566,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
@@ -5011,15 +5038,19 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"blake3",
|
||||
"image",
|
||||
"imageproc",
|
||||
"kamadak-exif",
|
||||
"kebab-config",
|
||||
"kebab-core",
|
||||
"kebab-llm",
|
||||
"kebab-llm-local",
|
||||
"ndarray",
|
||||
"ort",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -6423,6 +6454,21 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af"
|
||||
|
||||
[[package]]
|
||||
name = "nalgebra"
|
||||
version = "0.32.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"matrixmultiply",
|
||||
"num-complex",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"simba",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.18"
|
||||
@@ -8238,6 +8284,15 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "safetensors"
|
||||
version = "0.4.5"
|
||||
@@ -8615,6 +8670,19 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simba"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"num-complex",
|
||||
"num-traits",
|
||||
"paste",
|
||||
"wide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.9"
|
||||
@@ -10220,6 +10288,16 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "wide"
|
||||
version = "0.7.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"safe_arch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -35,6 +35,20 @@ kamadak-exif = "0.6"
|
||||
# transitive tokio runtime is brought in once.
|
||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
|
||||
base64 = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
# paddle-onnx OCR engine (PP-OCRv5, in-process). We reuse the workspace ort
|
||||
# pin (=2.0.0-rc.9) so the ONNX Runtime native lib stays single-versioned with
|
||||
# fastembed / kebab-nli (oar-ocr is intentionally NOT a dep — it would pull
|
||||
# ort rc.12 + ndarray 0.17, splitting the native `links` and threatening the
|
||||
# embedding stack). `download-binaries` extends the pin the same way
|
||||
# `kebab-nli/Cargo.toml:23` does: this crate isn't in fastembed's build graph,
|
||||
# so a standalone `cargo test -p kebab-parse-image` needs it to link onnxruntime.
|
||||
ort = { workspace = true, features = ["ndarray", "download-binaries"] }
|
||||
ndarray = { workspace = true }
|
||||
# imageproc: connected-components / contours for DBNet det post-processing.
|
||||
# min-area rotated-rect (rotating calipers) and polygon unclip are implemented
|
||||
# in pure Rust (clipper2 is C++ FFI — would break the single-binary guarantee).
|
||||
imageproc = "0.25"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = { workspace = true }
|
||||
|
||||
33
crates/kebab-parse-image/assets/paddleocr-onnx/NOTICE
Normal file
33
crates/kebab-parse-image/assets/paddleocr-onnx/NOTICE
Normal file
@@ -0,0 +1,33 @@
|
||||
PP-OCRv5 mobile ONNX models bundled with kebab (paddle-onnx OCR engine)
|
||||
=======================================================================
|
||||
|
||||
These model weights and the recognition dictionary are derived from
|
||||
PaddleOCR (https://github.com/PaddlePaddle/PaddleOCR), licensed under the
|
||||
Apache License, Version 2.0.
|
||||
|
||||
Copyright (c) PaddlePaddle Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use these files except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Files
|
||||
-----
|
||||
ppocrv5_mobile_det.onnx PP-OCRv5_mobile detection model (DBNet)
|
||||
korean_ppocrv5_mobile_rec.onnx korean_PP-OCRv5_mobile recognition model (CTC)
|
||||
korean_dict.txt recognition dictionary (11,945 chars: KR + Latin + digits + symbols)
|
||||
|
||||
These were converted from the official PaddlePaddle inference models to ONNX
|
||||
via paddle2onnx for in-process execution with onnxruntime (`ort`). No model
|
||||
architecture or weights were modified; only the serialization format changed.
|
||||
|
||||
The recognition CTC class layout (empirically confirmed, see
|
||||
tests/golden/ctc_rec_golden.json):
|
||||
index 0 = CTC blank
|
||||
index 1..11945 = korean_dict.txt line N -> class N (dict[N-1])
|
||||
index 11946 = space ' '
|
||||
total classes = 11947 (= 11945 dict + blank + space)
|
||||
|
||||
If any post-processing source (min-area-rect / polygon unclip) is later
|
||||
ported verbatim from oar-ocr (Apache-2.0), record the per-file provenance
|
||||
here as required by the Apache-2.0 attribution clause.
|
||||
11945
crates/kebab-parse-image/assets/paddleocr-onnx/korean_dict.txt
Normal file
11945
crates/kebab-parse-image/assets/paddleocr-onnx/korean_dict.txt
Normal file
File diff suppressed because it is too large
Load Diff
516
crates/kebab-parse-image/tests/golden/ctc_rec_golden.json
Normal file
516
crates/kebab-parse-image/tests/golden/ctc_rec_golden.json
Normal file
@@ -0,0 +1,516 @@
|
||||
{
|
||||
"dict_lines": 11945,
|
||||
"rec_classes": 11947,
|
||||
"blank_index": 0,
|
||||
"space_index": 11946,
|
||||
"mapping": "idx0=blank; idx 1..N=dict[idx-1]; idx N+1=space; classes=dict+2",
|
||||
"rec_norm": "RGB, /255 then (x-0.5)/0.5 => [-1,1], height=48 keep-aspect pad",
|
||||
"det_norm": "RGB, ImageNet mean/std *255 then /std, NCHW",
|
||||
"rec_cases": [
|
||||
{
|
||||
"text": "RAG 시스템 검색 결과",
|
||||
"decoded": "RAG시스템 검색 결과",
|
||||
"cer": 0.0769,
|
||||
"cer_nospace": 0.0,
|
||||
"mapping_ok": true,
|
||||
"T": 40,
|
||||
"C": 11947,
|
||||
"argmax_idx": [
|
||||
0,
|
||||
0,
|
||||
11553,
|
||||
0,
|
||||
11536,
|
||||
0,
|
||||
0,
|
||||
11542,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
6185,
|
||||
0,
|
||||
0,
|
||||
6129,
|
||||
0,
|
||||
0,
|
||||
9897,
|
||||
0,
|
||||
0,
|
||||
11946,
|
||||
0,
|
||||
461,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
5654,
|
||||
0,
|
||||
11946,
|
||||
0,
|
||||
509,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
585,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"collapsed_idx": [
|
||||
11553,
|
||||
11536,
|
||||
11542,
|
||||
6185,
|
||||
6129,
|
||||
9897,
|
||||
11946,
|
||||
461,
|
||||
5654,
|
||||
11946,
|
||||
509,
|
||||
585
|
||||
],
|
||||
"collapsed_conf": [
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002
|
||||
],
|
||||
"fired_timesteps": [
|
||||
2,
|
||||
4,
|
||||
7,
|
||||
11,
|
||||
14,
|
||||
17,
|
||||
20,
|
||||
22,
|
||||
26,
|
||||
28,
|
||||
30,
|
||||
34
|
||||
],
|
||||
"fired_logit_top5": [
|
||||
{
|
||||
"t": 2,
|
||||
"top5_idx": [
|
||||
11553,
|
||||
11583,
|
||||
11551,
|
||||
0,
|
||||
11541
|
||||
],
|
||||
"top5_val": [
|
||||
0.9998,
|
||||
0.0001,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 4,
|
||||
"top5_idx": [
|
||||
11536,
|
||||
11566,
|
||||
0,
|
||||
11748,
|
||||
11551
|
||||
],
|
||||
"top5_val": [
|
||||
0.9998,
|
||||
0.0001,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 7,
|
||||
"top5_idx": [
|
||||
11542,
|
||||
0,
|
||||
11572,
|
||||
11946,
|
||||
11585
|
||||
],
|
||||
"top5_val": [
|
||||
0.9994,
|
||||
0.0004,
|
||||
0.0001,
|
||||
0.0001,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 11,
|
||||
"top5_idx": [
|
||||
6185,
|
||||
0,
|
||||
11946,
|
||||
7949,
|
||||
11518
|
||||
],
|
||||
"top5_val": [
|
||||
0.9993,
|
||||
0.0003,
|
||||
0.0001,
|
||||
0.0001,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 14,
|
||||
"top5_idx": [
|
||||
6129,
|
||||
7893,
|
||||
0,
|
||||
9069,
|
||||
11536
|
||||
],
|
||||
"top5_val": [
|
||||
0.9997,
|
||||
0.0002,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 17,
|
||||
"top5_idx": [
|
||||
9897,
|
||||
9882,
|
||||
9889,
|
||||
9785,
|
||||
3429
|
||||
],
|
||||
"top5_val": [
|
||||
0.9999,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 20,
|
||||
"top5_idx": [
|
||||
11946,
|
||||
0,
|
||||
11516,
|
||||
11518,
|
||||
11579
|
||||
],
|
||||
"top5_val": [
|
||||
0.9026,
|
||||
0.0971,
|
||||
0.0002,
|
||||
0.0001,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 22,
|
||||
"top5_idx": [
|
||||
461,
|
||||
462,
|
||||
9281,
|
||||
349,
|
||||
0
|
||||
],
|
||||
"top5_val": [
|
||||
0.9995,
|
||||
0.0003,
|
||||
0.0001,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 26,
|
||||
"top5_idx": [
|
||||
5654,
|
||||
0,
|
||||
5766,
|
||||
8594,
|
||||
6830
|
||||
],
|
||||
"top5_val": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 28,
|
||||
"top5_idx": [
|
||||
11946,
|
||||
0,
|
||||
11516,
|
||||
11549,
|
||||
11564
|
||||
],
|
||||
"top5_val": [
|
||||
0.9422,
|
||||
0.0576,
|
||||
0.0001,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 30,
|
||||
"top5_idx": [
|
||||
509,
|
||||
0,
|
||||
453,
|
||||
11946,
|
||||
505
|
||||
],
|
||||
"top5_val": [
|
||||
0.9994,
|
||||
0.0004,
|
||||
0.0001,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 34,
|
||||
"top5_idx": [
|
||||
585,
|
||||
641,
|
||||
0,
|
||||
10329,
|
||||
589
|
||||
],
|
||||
"top5_val": [
|
||||
0.9999,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Embedding vector 0123",
|
||||
"decoded": "Embedding vector 0123",
|
||||
"cer": 0.0,
|
||||
"cer_nospace": 0.0,
|
||||
"mapping_ok": true,
|
||||
"T": 41,
|
||||
"C": 11947,
|
||||
"argmax_idx": [
|
||||
0,
|
||||
11540,
|
||||
0,
|
||||
0,
|
||||
11578,
|
||||
0,
|
||||
0,
|
||||
11567,
|
||||
0,
|
||||
11570,
|
||||
0,
|
||||
11569,
|
||||
0,
|
||||
11569,
|
||||
0,
|
||||
11574,
|
||||
0,
|
||||
11579,
|
||||
11572,
|
||||
11572,
|
||||
11946,
|
||||
0,
|
||||
11587,
|
||||
11570,
|
||||
0,
|
||||
11568,
|
||||
0,
|
||||
11585,
|
||||
11580,
|
||||
0,
|
||||
11583,
|
||||
11946,
|
||||
11946,
|
||||
11520,
|
||||
0,
|
||||
11521,
|
||||
0,
|
||||
11522,
|
||||
0,
|
||||
11523,
|
||||
0
|
||||
],
|
||||
"collapsed_idx": [
|
||||
11540,
|
||||
11578,
|
||||
11567,
|
||||
11570,
|
||||
11569,
|
||||
11569,
|
||||
11574,
|
||||
11579,
|
||||
11572,
|
||||
11946,
|
||||
11587,
|
||||
11570,
|
||||
11568,
|
||||
11585,
|
||||
11580,
|
||||
11583,
|
||||
11946,
|
||||
11520,
|
||||
11521,
|
||||
11522,
|
||||
11523
|
||||
],
|
||||
"collapsed_conf": [
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0001,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "한글 OCR 정확도 테스트",
|
||||
"decoded": "한글 OCR 정확도 테스트",
|
||||
"cer": 0.0,
|
||||
"cer_nospace": 0.0,
|
||||
"mapping_ok": true,
|
||||
"T": 41,
|
||||
"C": 11947,
|
||||
"argmax_idx": [
|
||||
0,
|
||||
0,
|
||||
10921,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
845,
|
||||
0,
|
||||
11946,
|
||||
0,
|
||||
11550,
|
||||
0,
|
||||
0,
|
||||
11538,
|
||||
0,
|
||||
11553,
|
||||
0,
|
||||
11946,
|
||||
0,
|
||||
7522,
|
||||
0,
|
||||
0,
|
||||
11170,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2321,
|
||||
0,
|
||||
11946,
|
||||
11946,
|
||||
9881,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
6129,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
10245,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"collapsed_idx": [
|
||||
10921,
|
||||
845,
|
||||
11946,
|
||||
11550,
|
||||
11538,
|
||||
11553,
|
||||
11946,
|
||||
7522,
|
||||
11170,
|
||||
2321,
|
||||
11946,
|
||||
9881,
|
||||
6129,
|
||||
10245
|
||||
],
|
||||
"collapsed_conf": [
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002,
|
||||
0.0002
|
||||
]
|
||||
}
|
||||
],
|
||||
"det_cases": [
|
||||
{
|
||||
"fixture": "clean_paragraph.png",
|
||||
"orig_hw": [
|
||||
192,
|
||||
900
|
||||
],
|
||||
"det_input_hw": [
|
||||
192,
|
||||
896
|
||||
],
|
||||
"prob_shape": [
|
||||
192,
|
||||
896
|
||||
],
|
||||
"prob_max": 1.0,
|
||||
"prob_mean": 0.1139,
|
||||
"positives_at_0.3": 19682,
|
||||
"positive_frac": 0.1144,
|
||||
"box_count": 3,
|
||||
"postproc": "thresh=0.3 -> findContours -> minAreaRect -> unclip(ratio=1.5, area*r/peri); box_thresh=0.5 mean-prob filter; coords scaled back to orig hw"
|
||||
}
|
||||
],
|
||||
"blank_index_confirmed_by_gt": true
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"fixture": "clean_paragraph.png",
|
||||
"orig_hw": [
|
||||
192,
|
||||
900
|
||||
],
|
||||
"det_input_hw": [
|
||||
192,
|
||||
896
|
||||
],
|
||||
"thresh": 0.3,
|
||||
"unclip_ratio": 1.5,
|
||||
"boxes": [
|
||||
{
|
||||
"poly": [
|
||||
[
|
||||
29,
|
||||
135
|
||||
],
|
||||
[
|
||||
615,
|
||||
134
|
||||
],
|
||||
[
|
||||
615,
|
||||
149
|
||||
],
|
||||
[
|
||||
29,
|
||||
150
|
||||
]
|
||||
],
|
||||
"score": 0.8724
|
||||
},
|
||||
{
|
||||
"poly": [
|
||||
[
|
||||
30,
|
||||
92
|
||||
],
|
||||
[
|
||||
597,
|
||||
92
|
||||
],
|
||||
[
|
||||
597,
|
||||
105
|
||||
],
|
||||
[
|
||||
30,
|
||||
105
|
||||
]
|
||||
],
|
||||
"score": 0.9627
|
||||
},
|
||||
{
|
||||
"poly": [
|
||||
[
|
||||
30,
|
||||
47
|
||||
],
|
||||
[
|
||||
509,
|
||||
47
|
||||
],
|
||||
[
|
||||
509,
|
||||
60
|
||||
],
|
||||
[
|
||||
30,
|
||||
60
|
||||
]
|
||||
],
|
||||
"score": 0.9304
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user