z230
This commit is contained in:
@@ -1378,5 +1378,49 @@
|
||||
{
|
||||
"original": "split_013.pdf",
|
||||
"corrected": "1006055083 2026-05-18 Šmíd, Jiří [EKG] [bez hodnocení].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5955100272 2022-06-29 Čulíková, Hana [LZ endokrinologie] [autoimunitní thyreoiditida ve fázi hypotyreózy, autoprotilátky pozit., substituce, arteriální hypertenze, dyslipidémie, zvýšená glykémie nalačno].pdf",
|
||||
"corrected": "5955100272 2022-06-29 Čulíková, Hana [LZ endokrinologie] [autoimunitní thyreoiditida ve fázi hypotyreózy, autoprotilátky pozit., substituce, arteriální hypertenze, dyslipidémie, zvýšená glykémie nalačno, ko +3m].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5955100272 2024-04-10 Čulíková, Hana [LZ endokrinologie] [seropozitivní AI thyreoiditida ve fázi hypotyreózy, substituce, arteriální hypertenze, dyslipidémie, zvýšená glykémie nalačno, osteopénie femur/krček/předloktí].pdf",
|
||||
"corrected": "5955100272 2024-04-10 Čulíková, Hana [LZ endokrinologie] [seropozitivní AI thyreoiditida ve fázi hypotyreózy, substituce, arteriální hypertenze, dyslipidémie, zvýšená glykémie nalačno, osteopénie femurkrčekpředloktí].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5955100272 Čulíková, Hana split_009.pdf",
|
||||
"corrected": "5955100272 2026-05-19 Čulíková, Hana [EKG] [bez hodnocení].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6804160990 2025-10-02 Pokorný, Bohumil [LZ chirurgie] [st.p. kýla klasicky 2008, TAPP 2018, tlaky a bolest L tříslo, recidivu nehmatám, sono + CT třísla, ko urol/ortop/koloskopie].pdf",
|
||||
"corrected": "6804160990 2025-10-02 Pokorný, Bohumil [LZ chirurgie] [st.p. kýla klasicky 2008, TAPP 2018, tlaky a bolest L tříslo, recidivu nehmatám, sono + CT třísla, ko urolortopkoloskopie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7261142658 2025-06-18 Šindelářová, Věra [LZ imunologie] [časté resp. infekce po COVID 11/2021, prick testy neg., spirometrie v normě, FeNO 19 PPB, ANA neg., IgE 19.8].pdf",
|
||||
"corrected": "7261142658 2025-06-18 Šindelářová, Věra [LZ imunologie] [časté resp. infekce po COVID 112021, prick testy neg., spirometrie v normě, FeNO 19 PPB, ANA neg., IgE 19.8].pdf"
|
||||
},
|
||||
{
|
||||
"original": "315620122 2025-12-19 Tesaříková, Květuše [LZ endokrinologie] [hypotyreóza, TSH 2.514, fT4 17.84, Euthyrox 50ug, ekzém DKK, DEXA 5/2026].pdf",
|
||||
"corrected": "315620122 2025-12-19 Tesaříková, Květuše [LZ endokrinologie] [hypotyreóza, TSH 2.514, fT4 17.84, Euthyrox 50ug, ekzém DKK, DEXA 52026].pdf"
|
||||
},
|
||||
{
|
||||
"original": "315620122 2026-01-14 Tesaříková, Květuše [LZ kardiologie] [EF 65%, lehká porucha diastol. funkce, mitrální regurgitace 3/4, hraniční PK].pdf",
|
||||
"corrected": "315620122 2026-01-14 Tesaříková, Květuše [LZ kardiologie] [EF 65%, lehká porucha diastol. funkce, mitrální regurgitace 34, hraniční PK, ko JUN2026].pdf"
|
||||
},
|
||||
{
|
||||
"original": "515803110 2025-12-16 Rygerová, Lenka [Laboratoř] [dg. J069 - CKD-EPI CHRIG5, leukocyty 13.1, MCH 27.4, trombocyty 521, RF 24, ESR 78].pdf",
|
||||
"corrected": "515803110 2025-12-16 Rygerová, Lenka [Laboratoř] [dg. J069 - CKD-EPI CHRIG2, leukocyty 13.1, MCH 27.4, trombocyty 521, RF 24, ESR 78].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6860090446 2025-12-15 Diepoldová, Kateřina [Laboratoř] [dg. K29 - CKD-EPI CHRIG3b, MCV 80.0 (↓), MCH 27.8 (↓)].pdf",
|
||||
"corrected": "6860090446 2025-12-15 Diepoldová, Kateřina [Laboratoř] [dg. K29 - CKD-EPI CHRIG2, MCV 80.0 (↓), MCH 27.8 (↓)].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7153180551 2025-12-15 Šamšová, Irena [Laboratoř] [dg. J069 - Anti HAV total 25.22 IU/L (↑), pacient s ochrannou hladinou protilátek].pdf",
|
||||
"corrected": "7153180551 2025-12-15 Šamšová, Irena [Laboratoř] [dg. J069 - Anti HAV total 25.22 IUL (↑), pacient s ochrannou hladinou protilátek].pdf"
|
||||
},
|
||||
{
|
||||
"original": "9959130423 2025-12-17 Sládková, Aneta [Laboratoř] [dg. N309 - E. coli 10E6 CFU/ml, citlivá na ampicilin, cefuroxim, cotrimoxazol, nitrofurantoin, fosfomycin].pdf",
|
||||
"corrected": "9959130423 2025-12-17 Sládková, Aneta [Laboratoř] [dg. N309 - E. coli 10E6 CFUml, citlivá na ampicilin, cefuroxim, cotrimoxazol, nitrofurantoin, fosfomycin].pdf"
|
||||
}
|
||||
]
|
||||
@@ -13,7 +13,13 @@ Tato pravidla platí vždy při generování polí `poznamka` a `nazev_souboru`.
|
||||
- Pokud datum hospitalizace nelze určit, druhou závorku napiš bez datumu.
|
||||
|
||||
3. Když je dokument typ "Laboratoř", do `poznamka` uváděj POUZE hodnoty mimo normu (patologické nálezy) — hodnoty v normě vynech. **Osmolalitu séra (Osmolalita, Osm, osmolality) NIKDY nezmiňuj — ani když je mimo normu, ani v jakékoli zkratce.** Toto je absolutní výjimka: osmolalita se do názvu souboru ani do poznámky nepíše nikdy za žádných okolností. Chybně: `C_Osmolalita 293 (↑)` — správně: tuto hodnotu zcela vynech.
|
||||
4. Pokud laboratorní výsledky obsahují glomerulární filtraci — bývá označena jako eGFR, CKD-EPI nebo CK-EPI — do `poznamka` nikdy nepiš číselnou hodnotu eGFR. Místo toho uveď pouze klasifikaci: eGFR ≥ 90 → CHRIG1, 60–89 → CHRIG2, 45–59 → CHRIG3a, 30–44 → CHRIG3b, 15–29 → CHRIG4, < 15 → CHRIG5. Klasifikaci uváděj pouze pokud je CHRIG2 nebo horší (tj. eGFR < 90) — CHRIG1 je v normě, nezmiňuj ho.
|
||||
4. Pokud laboratorní výsledky obsahují glomerulární filtraci — bývá označena jako eGFR, CKD-EPI nebo CK-EPI — do `poznamka` nikdy nepiš číselnou hodnotu eGFR. Místo toho uveď pouze klasifikaci dle stadií CHRIG1–CHRIG5.
|
||||
- **Jednotka:** Nejprve zkontroluj jednotku uvedenou v laboratoři:
|
||||
- Pokud je hodnota v **ml/s** nebo **ml/sec** (typicky malá čísla jako 0.8, 1.14, 1.5…), přenásob ×60 pro převod na ml/min.
|
||||
- Pokud je hodnota v **ml/min** nebo **ml/min/1.73m²** (typicky velká čísla jako 55, 68, 90…), použij přímo.
|
||||
- **Klasifikace** (v ml/min/1.73m²): ≥ 90 → CHRIG1, 60–89 → CHRIG2, 45–59 → CHRIG3a, 30–44 → CHRIG3b, 15–29 → CHRIG4, < 15 → CHRIG5.
|
||||
- Prahové hodnoty pro orientaci při jednotce ml/s: ≥ 1.50 → G1, 1.00–1.49 → G2, 0.75–0.99 → G3a, 0.50–0.74 → G3b, 0.25–0.49 → G4, < 0.25 → G5.
|
||||
- Klasifikaci uváděj pouze pokud je CHRIG2 nebo horší (tj. eGFR < 90 ml/min nebo < 1.50 ml/s) — CHRIG1 je v normě, nezmiňuj ho.
|
||||
5. Když je dokument typ "Laboratoř" a zpráva obsahuje diagnózu (dg., dg:, diagnóza), umísti ji do `nazev_souboru` jako první část druhé závorky, tedy: `[Laboratoř] [dg. XY00 - stručná poznamka]`.
|
||||
6. Zkratky a pojmenování: slovo „sono" (sonografie/ultrazvuk) piš vždy malými písmeny — `sono břicha`, `sono ŠŽ`, nikoli `SONO`. Štítnou žlázu označuj vždy zkratkou `ŠŽ`. Sonografii prsu/prsů (sono mamm., sono mamografie, sono mamma apod.) piš vždy jako `sono prsů`. Denzitometrii (DEXA, DXA, denzitometrie) piš vždy pouze jako `[DXA]` — bez prefixu LZ. Algologii piš vždy jako `[LZ léčba bolesti]`. Dermatovenerologii (dermatologie, dermatovenerologie, kožní) piš vždy jako `[LZ kožní]`. Angiologii piš vždy jako `[LZ cévní]`.
|
||||
7. V číselných hodnotách VŽDY používej desetinnou tečku, nikoli desetinnou čárku. Toto pravidlo platí absolutně pro všechna čísla v `poznamka` i `nazev_souboru` — např. `TG 4.73`, nikoli `TG 4,73`.
|
||||
|
||||
@@ -29,6 +29,7 @@ Pro vývoj: `TESTOVANI = True` + `PATH_TO_TESTFILE` na začátku skriptu.
|
||||
| `/` | otočit CCW (counterclockwise) |
|
||||
| `*` | otočit CW (clockwise) |
|
||||
| `Del` / `.` | smaž stránku (vynech z exportu) |
|
||||
| `0` | znovu spustí OCR na aktuální stránce (smaže cache pro tuto stránku) |
|
||||
| `Enter` | exportuj všechny skupiny |
|
||||
| `Esc` | konec |
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ Numerická klávesnice:
|
||||
5 / Space přepni hranici pacienta před touto stránkou
|
||||
8 / Up přesuň stránku doleva (swap)
|
||||
2 / Down přesuň stránku doprava (swap)
|
||||
0 znovu spustí OCR na aktuální stránce (smaže cache pro tuto stránku)
|
||||
- výběr pacienta ručně z Medicusu
|
||||
Enter exportuj všechny skupiny do Split/
|
||||
Esc konec
|
||||
@@ -271,63 +272,76 @@ class OcrWorker:
|
||||
def stop(self):
|
||||
self._stop.set()
|
||||
|
||||
def _run(self):
|
||||
def _ocr_page(self, i: int):
|
||||
"""Spustí OCR pipeline pro stránku i a uloží výsledek do self.results."""
|
||||
import pytesseract
|
||||
pytesseract.pytesseract.tesseract_cmd = TESSERACT_PATH
|
||||
|
||||
n = len(self.doc)
|
||||
for i in range(n):
|
||||
page = self.doc[i]
|
||||
mat = fitz.Matrix(2.0, 2.0) # 144 DPI — dostatečné pro OCR
|
||||
pix = page.get_pixmap(matrix=mat, colorspace=fitz.csRGB)
|
||||
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
|
||||
|
||||
# 1. Tesseract
|
||||
rc = None
|
||||
tess_text = None
|
||||
try:
|
||||
tess_text = pytesseract.image_to_string(img, lang="ces")
|
||||
rc = _extract_rc(tess_text)
|
||||
except Exception as e:
|
||||
print(f"[OCR str.{i+1}] Tesseract: {e}")
|
||||
|
||||
# 2. Medicus — první pokus
|
||||
medicus = _verify_medicus(rc) if rc else None
|
||||
|
||||
# 3. Claude Vision — když Tesseract nenašel RČ, nebo našel ale Medicus nezná
|
||||
claude_raw = None
|
||||
claude_usage = None
|
||||
if not rc or (medicus and medicus.get("status") == "not_found"):
|
||||
try:
|
||||
rc_claude, claude_raw, claude_usage = self._claude_rc(img)
|
||||
if rc_claude:
|
||||
medicus_claude = _verify_medicus(rc_claude)
|
||||
if medicus_claude.get("status") in ("ok", "fuzzy"):
|
||||
print(f"[OCR str.{i+1}] Claude opravil RČ: {rc} → {rc_claude}")
|
||||
rc = rc_claude
|
||||
medicus = medicus_claude
|
||||
elif not rc:
|
||||
rc = rc_claude
|
||||
medicus = medicus_claude
|
||||
except Exception as e:
|
||||
print(f"[OCR str.{i+1}] Claude: {e}")
|
||||
|
||||
result = {
|
||||
"rc": rc,
|
||||
"medicus": medicus,
|
||||
"tesseract_text": tess_text,
|
||||
"claude_raw": claude_raw,
|
||||
"claude_usage": claude_usage,
|
||||
}
|
||||
with self._lock:
|
||||
self.results[i] = result
|
||||
|
||||
def _run(self):
|
||||
for i in range(len(self.doc)):
|
||||
if self._stop.is_set():
|
||||
break
|
||||
if i in self.results:
|
||||
continue # cache hit
|
||||
|
||||
page = self.doc[i]
|
||||
mat = fitz.Matrix(2.0, 2.0) # 144 DPI — dostatečné pro OCR
|
||||
pix = page.get_pixmap(matrix=mat, colorspace=fitz.csRGB)
|
||||
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
|
||||
|
||||
# 1. Tesseract
|
||||
rc = None
|
||||
tess_text = None
|
||||
try:
|
||||
tess_text = pytesseract.image_to_string(img, lang="ces")
|
||||
rc = _extract_rc(tess_text)
|
||||
except Exception as e:
|
||||
print(f"[OCR str.{i+1}] Tesseract: {e}")
|
||||
|
||||
# 2. Medicus — první pokus
|
||||
medicus = _verify_medicus(rc) if rc else None
|
||||
|
||||
# 3. Claude Vision — když Tesseract nenašel RČ, nebo našel ale Medicus nezná
|
||||
claude_raw = None
|
||||
claude_usage = None
|
||||
if not rc or (medicus and medicus.get("status") == "not_found"):
|
||||
try:
|
||||
rc_claude, claude_raw, claude_usage = self._claude_rc(img)
|
||||
if rc_claude:
|
||||
medicus_claude = _verify_medicus(rc_claude)
|
||||
if medicus_claude.get("status") in ("ok", "fuzzy"):
|
||||
print(f"[OCR str.{i+1}] Claude opravil RČ: {rc} → {rc_claude}")
|
||||
rc = rc_claude
|
||||
medicus = medicus_claude
|
||||
elif not rc:
|
||||
rc = rc_claude
|
||||
medicus = medicus_claude
|
||||
except Exception as e:
|
||||
print(f"[OCR str.{i+1}] Claude: {e}")
|
||||
|
||||
result = {
|
||||
"rc": rc,
|
||||
"medicus": medicus,
|
||||
"tesseract_text": tess_text,
|
||||
"claude_raw": claude_raw,
|
||||
"claude_usage": claude_usage,
|
||||
}
|
||||
self.results[i] = result
|
||||
self._ocr_page(i)
|
||||
self._save_cache()
|
||||
self.on_page_done(i)
|
||||
|
||||
def rerun_page(self, page_idx: int, on_done):
|
||||
"""Znovu spustí OCR pro jednu stránku (ignoruje cache). Volá on_done(page_idx) po dokončení."""
|
||||
def _worker():
|
||||
with self._lock:
|
||||
self.results.pop(page_idx, None)
|
||||
self._ocr_page(page_idx)
|
||||
self._save_cache()
|
||||
on_done(page_idx)
|
||||
threading.Thread(target=_worker, daemon=True).start()
|
||||
|
||||
def _claude_rc(self, img: Image.Image) -> tuple[Optional[str], Optional[str], Optional[dict]]:
|
||||
import anthropic, base64
|
||||
|
||||
@@ -633,7 +647,7 @@ class SplitterUI:
|
||||
"1/3: přesuň stránku "
|
||||
"/: otočit ↺CCW *: otočit ↻CW "
|
||||
"Del/.: smaž stránku "
|
||||
"-: vyber pacienta ručně "
|
||||
"0: znovu OCR -: vyber pacienta ručně "
|
||||
"Enter: exportuj Esc: konec"
|
||||
)
|
||||
self.bot_label = tk.Label(
|
||||
@@ -738,6 +752,7 @@ class SplitterUI:
|
||||
103: "num7", 105: "num9",
|
||||
97: "num1", 99: "num3", 110: "numdot",
|
||||
111: "numslash", 106: "numstar", 109: "numminus",
|
||||
96: "num0",
|
||||
}
|
||||
action = numpad.get(kc) or {
|
||||
"Left": "num4", "Right": "num6",
|
||||
@@ -748,6 +763,7 @@ class SplitterUI:
|
||||
"KP_Divide": "numslash", "KP_Multiply": "numstar",
|
||||
"slash": "numslash", "asterisk": "numstar",
|
||||
"KP_Subtract": "numminus", "minus": "numminus",
|
||||
"Insert": "num0", "KP_Insert": "num0",
|
||||
}.get(ks)
|
||||
|
||||
if action == "num4":
|
||||
@@ -772,6 +788,8 @@ class SplitterUI:
|
||||
self._delete_page()
|
||||
elif action == "numminus":
|
||||
self._open_patient_picker()
|
||||
elif action == "num0":
|
||||
self._rerun_ocr_current()
|
||||
elif ks in ("Return", "KP_Enter"):
|
||||
self._export()
|
||||
elif ks == "Escape":
|
||||
@@ -843,6 +861,15 @@ class SplitterUI:
|
||||
|
||||
PatientPickerDialog(self.root, on_select)
|
||||
|
||||
def _rerun_ocr_current(self):
|
||||
page_idx = self.page_order[self.cursor]
|
||||
self.ocr_results.pop(page_idx, None)
|
||||
self._redraw()
|
||||
self.ocr_worker.rerun_page(
|
||||
page_idx,
|
||||
on_done=lambda idx: self.root.after(0, self._on_ocr_done, idx),
|
||||
)
|
||||
|
||||
def _update_boundaries_around(self, pos: int):
|
||||
"""Přidá/odstraní hranice kolem pozice pos podle potvrzených pacientů."""
|
||||
def confirmed_rc(p: int) -> Optional[str]:
|
||||
|
||||
Reference in New Issue
Block a user