/
O.S.Prog
/
DiScan
Обзор
Документация
Войти
/
O.S.Prog
/
DiScan
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
utils/encoding.py
197 строк
7 KB
O.S.Prog
Initial commit: DiScan v1.1.0
18 июл 2026, 15:51
18 июл 2026, 15:51
238a6ec
Код
Авторство
О чём код?
# utils/encoding.py """ Работа с кодировками DICOM """ import pydicom from typing import Optional class DICOMEncoding: """Обработка кодировок DICOM""" # Список кодировок для кириллицы в порядке приоритета CYRILLIC_ENCODINGS = [ 'cp1251', # Windows-1251 - самый распространенный 'mac-cyrillic', # Mac Cyrillic 'koi8-r', 'iso-8859-5', 'cp866', # DOS Cyrillic 'utf-8', ] @staticmethod def decode_patient_name(ds) -> Optional[str]: """ Декодирует имя пациента из DICOM-датасета """ if not hasattr(ds, 'PatientName') or not ds.PatientName: return None patient_name = ds.PatientName # Если это PersonName из pydicom if hasattr(patient_name, 'encoded_bytes'): raw_bytes = patient_name.encoded_bytes if raw_bytes: # Пробуем декодировать байты decoded = DICOMEncoding._decode_bytes(raw_bytes, ds) if decoded and DICOMEncoding._is_valid_cyrillic(decoded): return decoded # Если это строка if isinstance(patient_name, str): # Проверяем, не содержит ли строка уже нормальную кириллицу if DICOMEncoding._is_valid_cyrillic(patient_name): return patient_name # Пробуем декодировать строку через разные кодировки for enc in DICOMEncoding.CYRILLIC_ENCODINGS: try: # Пробуем разные способы конвертации # Способ 1: латиница -> нужная кодировка try: decoded = patient_name.encode('latin1').decode(enc) if DICOMEncoding._is_valid_cyrillic(decoded): return decoded except: pass # Способ 2: напрямую декодировать байты try: raw = patient_name.encode('utf-8') decoded = raw.decode(enc) if DICOMEncoding._is_valid_cyrillic(decoded): return decoded except: pass except: continue # Если имя короткое (типа "проба") - возвращаем как есть if len(patient_name) < 20: return patient_name return patient_name # Если это bytes if isinstance(patient_name, bytes): return DICOMEncoding._decode_bytes(patient_name, ds) # В крайнем случае - строковое представление try: return str(patient_name) except: return None @staticmethod def _decode_bytes(raw_bytes: bytes, ds=None) -> str: """Декодирует байты в строку с определением кодировки""" # 1. Пробуем получить кодировку из DICOM-тега if ds and hasattr(ds, 'SpecificCharacterSet'): try: char_set = str(ds.SpecificCharacterSet) python_enc = DICOMEncoding._dicom_to_python_encoding(char_set) if python_enc: try: decoded = raw_bytes.decode(python_enc) if DICOMEncoding._is_valid_cyrillic(decoded): return decoded except: pass except: pass # 2. Пробуем все кириллические кодировки for enc in DICOMEncoding.CYRILLIC_ENCODINGS: try: decoded = raw_bytes.decode(enc) if DICOMEncoding._is_valid_cyrillic(decoded): return decoded except: continue # 3. Специальная попытка для битых UTF-8 в Latin-1 try: latin1_str = raw_bytes.decode('latin1') if any(char in latin1_str for char in ['Ã', 'Â', 'ï', 'ð', 'î', 'á']): utf8_bytes = latin1_str.encode('latin1') decoded = utf8_bytes.decode('utf-8', errors='replace') if DICOMEncoding._is_valid_cyrillic(decoded): return decoded except: pass # 4. Пробуем через pydicom try: from pydicom.valuerep import PersonName decoded = str(PersonName(raw_bytes)) if DICOMEncoding._is_valid_cyrillic(decoded): return decoded except: pass # 5. Если ничего не помогло, возвращаем как есть try: return raw_bytes.decode('utf-8', errors='replace') except: return str(raw_bytes) @staticmethod def _dicom_to_python_encoding(dicom_encoding: str) -> Optional[str]: """Преобразует DICOM-кодировку в Python-кодировку""" encoding_map = { 'ISO_IR 100': 'latin1', 'ISO_IR 144': 'cp1251', 'ISO_IR 148': 'cp1251', 'ISO_IR 6': 'ascii', 'UTF-8': 'utf-8', 'ISO_IR 101': 'latin2', 'ISO_IR 109': 'latin3', 'ISO_IR 110': 'latin4', 'ISO_IR 126': 'greek', 'ISO_IR 127': 'arabic', 'ISO_IR 128': 'hebrew', 'ISO_IR 129': 'latin5', 'ISO_IR 138': 'latin6', 'ISO_IR 166': 'tis620', 'ISO_IR 192': 'utf-8', 'GB18030': 'gb18030', 'GBK': 'gbk', 'Big5': 'big5', 'Shift-JIS': 'shift_jis', } if isinstance(dicom_encoding, list): dicom_encoding = dicom_encoding[0] if dicom_encoding else '' return encoding_map.get(str(dicom_encoding), None) @staticmethod def _is_valid_cyrillic(text: str) -> bool: """Проверяет, содержит ли текст кириллицу и читаемый ли он""" if not text: return False # Проверяем наличие кириллических символов cyrillic_chars = any( ('\u0400' <= char <= '\u04FF') or ('\u0500' <= char <= '\u052F') for char in text ) if cyrillic_chars: if '\uFFFD' in text: return False question_marks = text.count('?') if question_marks > 0 and len(text) > 1: if question_marks / len(text) > 0.2: return False return True # Если кириллицы нет, но текст короткий и читаемый - считаем валидным if len(text) < 30 and text.isprintable(): return True return False