Skip to content

Commit a2b7ab9

Browse files
authored
Add offline CATT diacritization (encoder only) (#3647)
1 parent fb4d154 commit a2b7ab9

33 files changed

Lines changed: 1825 additions & 0 deletions
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env bash
2+
3+
set -ex
4+
5+
log() {
6+
# This function is from espnet
7+
local fname=${BASH_SOURCE[1]##*/}
8+
echo -e "$(date '+%Y-%m-%d %H:%M:%S') (${fname}:${BASH_LINENO[0]}:${FUNCNAME[1]}) $*"
9+
}
10+
11+
echo "EXE is $EXE"
12+
echo "PATH: $PATH"
13+
14+
which $EXE
15+
16+
log "------------------------------------------------------------"
17+
log "Download the CATT (Encoder-Only) diacritization model "
18+
log "------------------------------------------------------------"
19+
20+
tmp_dir=catt_eo_model_onnx
21+
mkdir -p $tmp_dir
22+
cd $tmp_dir
23+
curl -SL -O https://github.com/abjadai/catt/releases/download/v2/eo_model_onnx.zip
24+
unzip -o eo_model_onnx.zip
25+
rm eo_model_onnx.zip
26+
cd ..
27+
28+
assert_diacritized() {
29+
local input="$1"
30+
local output="$2"
31+
python - "$input" "$output" <<'PY'
32+
import sys
33+
inp, out = sys.argv[1], sys.argv[2]
34+
assert out, "output is empty"
35+
assert out != inp, "output equals input (no diacritization applied)"
36+
# 0x064B fathatan, 0x0652 sukun, 0x0670 superscript alef
37+
tashkeel = set(range(0x064B, 0x0653)) | {0x0670}
38+
assert any(ord(c) in tashkeel for c in out), \
39+
"output contains no tashkeel codepoint"
40+
print("OK")
41+
PY
42+
}
43+
44+
first_input="وقالت مجلة نيوزويك الأمريكية التحديث الجديد ل إنستجرام يمكن أن يساهم في إيقاف وكشف الحسابات المزورة بسهولة شديدة"
45+
first_output=$($EXE \
46+
--debug=1 \
47+
--catt-encoder=$tmp_dir/encoder.onnx \
48+
--catt-decoder=$tmp_dir/decoder.onnx \
49+
"$first_input")
50+
assert_diacritized "$first_input" "$first_output"
51+
52+
second_input="اللغة العربية من أقدم اللغات السامية"
53+
second_output=$($EXE \
54+
--debug=1 \
55+
--catt-encoder=$tmp_dir/encoder.onnx \
56+
--catt-decoder=$tmp_dir/decoder.onnx \
57+
"$second_input")
58+
assert_diacritized "$second_input" "$second_output"
59+
60+
rm -rf $tmp_dir

.github/scripts/test-python.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,16 @@ python3 ./python-api-examples/add-punctuation.py
341341

342342
rm -rf $repo
343343

344+
log "test offline diacritization"
345+
346+
curl -SL -O https://github.com/abjadai/catt/releases/download/v2/eo_model_onnx.zip
347+
unzip eo_model_onnx.zip -d catt_eo_model_onnx
348+
rm eo_model_onnx.zip
349+
350+
python3 ./python-api-examples/add-diacritics.py
351+
352+
rm -rf catt_eo_model_onnx
353+
344354
log "test online punctuation"
345355

346356
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/punctuation-models/sherpa-onnx-online-punct-en-2024-08-06.tar.bz2

.github/workflows/linux.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ on:
1919
- '.github/scripts/test-online-ctc.sh'
2020
- '.github/scripts/test-offline-tts.sh'
2121
- '.github/scripts/test-audio-tagging.sh'
22+
- '.github/scripts/test-offline-diacritization.sh'
2223
- '.github/scripts/test-offline-punctuation.sh'
2324
- '.github/scripts/test-online-punctuation.sh'
2425
- '.github/scripts/test-speaker-diarization.sh'
@@ -44,6 +45,7 @@ on:
4445
- '.github/scripts/test-online-ctc.sh'
4546
- '.github/scripts/test-offline-tts.sh'
4647
- '.github/scripts/test-audio-tagging.sh'
48+
- '.github/scripts/test-offline-diacritization.sh'
4749
- '.github/scripts/test-offline-punctuation.sh'
4850
- '.github/scripts/test-online-punctuation.sh'
4951
- '.github/scripts/test-speaker-diarization.sh'
@@ -468,6 +470,17 @@ jobs:
468470
.github/scripts/test-offline-punctuation.sh
469471
du -h -d1 .
470472
473+
- name: Test offline diacritization
474+
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
475+
shell: bash
476+
run: |
477+
du -h -d1 .
478+
export PATH=$PWD/build/bin:$PATH
479+
export EXE=sherpa-onnx-offline-diacritization
480+
481+
.github/scripts/test-offline-diacritization.sh
482+
du -h -d1 .
483+
471484
- name: Test Audio tagging
472485
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
473486
shell: bash

.github/workflows/macos.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ on:
1818
- '.github/scripts/test-offline-tts.sh'
1919
- '.github/scripts/test-online-ctc.sh'
2020
- '.github/scripts/test-audio-tagging.sh'
21+
- '.github/scripts/test-offline-diacritization.sh'
2122
- '.github/scripts/test-offline-punctuation.sh'
2223
- '.github/scripts/test-online-punctuation.sh'
2324
- '.github/scripts/test-speaker-diarization.sh'
@@ -41,6 +42,7 @@ on:
4142
- '.github/scripts/test-offline-tts.sh'
4243
- '.github/scripts/test-online-ctc.sh'
4344
- '.github/scripts/test-audio-tagging.sh'
45+
- '.github/scripts/test-offline-diacritization.sh'
4446
- '.github/scripts/test-offline-punctuation.sh'
4547
- '.github/scripts/test-online-punctuation.sh'
4648
- '.github/scripts/test-speaker-diarization.sh'
@@ -342,6 +344,15 @@ jobs:
342344
343345
.github/scripts/test-offline-punctuation.sh
344346
347+
- name: Test offline diacritization
348+
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
349+
shell: bash
350+
run: |
351+
export PATH=$PWD/build/bin:$PATH
352+
export EXE=sherpa-onnx-offline-diacritization
353+
354+
.github/scripts/test-offline-diacritization.sh
355+
345356
- name: Test C API
346357
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
347358
shell: bash

.github/workflows/sanitizer.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ jobs:
117117
118118
.github/scripts/test-offline-punctuation.sh
119119
120+
- name: Test offline diacritization
121+
shell: bash
122+
run: |
123+
export PATH=$PWD/build/bin:$PATH
124+
export EXE=sherpa-onnx-offline-diacritization
125+
126+
.github/scripts/test-offline-diacritization.sh
127+
120128
- name: Test offline transducer
121129
shell: bash
122130
run: |

.github/workflows/windows-x64.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
- '.github/scripts/test-online-transducer.sh'
1212
- '.github/scripts/test-online-paraformer.sh'
1313
- '.github/scripts/test-offline-cohere-transcribe.sh'
14+
- '.github/scripts/test-offline-diacritization.sh'
1415
- '.github/scripts/test-offline-transducer.sh'
1516
- '.github/scripts/test-offline-ctc.sh'
1617
- '.github/scripts/test-online-ctc.sh'
@@ -332,6 +333,15 @@ jobs:
332333
333334
.github/scripts/test-offline-punctuation.sh
334335
336+
- name: Test offline diacritization
337+
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
338+
shell: bash
339+
run: |
340+
export PATH=$PWD/build/bin/${{ matrix.build_type }}:$PATH
341+
export EXE=sherpa-onnx-offline-diacritization.exe
342+
343+
.github/scripts/test-offline-diacritization.sh
344+
335345
- name: Test C API
336346
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
337347
shell: bash

cmake/cmake_extension.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def get_binaries():
6262
"sherpa-onnx-offline",
6363
"sherpa-onnx-offline-audio-tagging",
6464
"sherpa-onnx-offline-denoiser",
65+
"sherpa-onnx-offline-diacritization",
6566
"sherpa-onnx-offline-language-identification",
6667
"sherpa-onnx-offline-punctuation",
6768
"sherpa-onnx-offline-source-separation",

cxx-api-examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ target_link_libraries(offline-speaker-diarization-cxx-api sherpa-onnx-cxx-api)
103103
add_executable(cohere-transcribe-cxx-api ./cohere-transcribe-cxx-api.cc)
104104
target_link_libraries(cohere-transcribe-cxx-api sherpa-onnx-cxx-api)
105105

106+
add_executable(offline-diacritization-cxx-api ./offline-diacritization-cxx-api.cc)
107+
target_link_libraries(offline-diacritization-cxx-api sherpa-onnx-cxx-api)
108+
106109
add_executable(offline-punctuation-cxx-api ./offline-punctuation-cxx-api.cc)
107110
target_link_libraries(offline-punctuation-cxx-api sherpa-onnx-cxx-api)
108111

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// cxx-api-examples/offline-diacritization-cxx-api.cc
2+
// Copyright (c) 2026 Matias Lin
3+
4+
// To use the CATT (Encoder-Only) diacritization model:
5+
// clang-format off
6+
// wget https://github.com/abjadai/catt/releases/download/v2/eo_model_onnx.zip
7+
// unzip eo_model_onnx.zip -d catt_eo_model_onnx
8+
// rm eo_model_onnx.zip
9+
// clang-format on
10+
11+
#include <iostream>
12+
#include <string>
13+
14+
#include "sherpa-onnx/c-api/cxx-api.h"
15+
16+
int32_t main() {
17+
using namespace sherpa_onnx::cxx; // NOLINT
18+
19+
OfflineDiacritizationConfig diacritization_config;
20+
diacritization_config.model.catt_encoder =
21+
"./catt_eo_model_onnx/encoder.onnx";
22+
diacritization_config.model.catt_decoder =
23+
"./catt_eo_model_onnx/decoder.onnx";
24+
diacritization_config.model.num_threads = 1;
25+
diacritization_config.model.debug = false;
26+
diacritization_config.model.provider = "cpu";
27+
28+
OfflineDiacritization diacrt =
29+
OfflineDiacritization::Create(diacritization_config);
30+
if (!diacrt.Get()) {
31+
std::cerr
32+
<< "Failed to create diacritization model. Please check your config\n";
33+
return -1;
34+
}
35+
36+
std::string text = "اللغة العربية من أقدم اللغات السامية";
37+
std::string text_with_diacritics = diacrt.AddDiacritics(text);
38+
std::cout << "Original text: " << text << std::endl;
39+
std::cout << "With diacritics: " << text_with_diacritics << std::endl;
40+
41+
return 0;
42+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2026 Matias Lin
4+
5+
"""
6+
This script shows how to add diacritics (tashkeel) to Arabic text
7+
using the sherpa-onnx Python API.
8+
9+
It uses the CATT (Context-Aware Transformer for Tashkeel) Encoder-Only
10+
checkpoint, which exports two ONNX files: an encoder and a decoder.
11+
12+
To use the CATT (Encoder-Only) diacritization model:
13+
14+
wget https://github.com/abjadai/catt/releases/download/v2/eo_model_onnx.zip
15+
unzip eo_model_onnx.zip -d catt_eo_model_onnx
16+
rm eo_model_onnx.zip
17+
"""
18+
19+
from pathlib import Path
20+
21+
import sherpa_onnx
22+
23+
24+
def main():
25+
catt_encoder = "./catt_eo_model_onnx/encoder.onnx"
26+
catt_decoder = "./catt_eo_model_onnx/decoder.onnx"
27+
28+
for f in (catt_encoder, catt_decoder):
29+
if not Path(f).is_file():
30+
raise ValueError(f"{f} does not exist")
31+
32+
config = sherpa_onnx.OfflineDiacritizationConfig(
33+
model=sherpa_onnx.OfflineDiacritizationModelConfig(
34+
catt_encoder=catt_encoder,
35+
catt_decoder=catt_decoder,
36+
),
37+
)
38+
39+
diacrt = sherpa_onnx.OfflineDiacritization(config)
40+
41+
text_list = [
42+
"وقالت مجلة نيوزويك الأمريكية التحديث الجديد ل إنستجرام يمكن أن يساهم في إيقاف وكشف الحسابات المزورة بسهولة شديدة",
43+
"اللغة العربية من أقدم اللغات السامية",
44+
]
45+
for text in text_list:
46+
text_with_diacritics = diacrt.add_diacritics(text)
47+
print("----------")
48+
print(f"input: {text}")
49+
print(f"output: {text_with_diacritics}")
50+
51+
print("----------")
52+
53+
54+
if __name__ == "__main__":
55+
main()

0 commit comments

Comments
 (0)