fix: sanitize noisy OCR output for OPD scan AI results

- add sanitizeExtractedScanText() to clean OCR text before analysis/storage
- strip non-ASCII/control noise that appears as mixed-script junk characters
- remove repeated junk tokens (e.g. ffffff, repeated words)
- add ECG-specific OCR normalization (KECG->ECG, Uniconfirmed->Unconfirmed, avt->aVL, VT->V1)
- apply sanitization to both OCR sources (document intelligence and vision fallback)
- keep fallback behavior when sanitization returns empty
This commit is contained in:
DevSoftTech Server 2026-03-31 05:12:56 +05:30
parent cf299de505
commit f7b9a51c47

View File

@ -4584,18 +4584,63 @@ class Opd extends BaseController
$docIntelText = $this->extractTextWithAzureDocumentIntelligence($filePath);
if ($docIntelText !== '') {
return preg_replace('/\s+/', ' ', trim($docIntelText)) ?: trim($docIntelText);
$clean = $this->sanitizeExtractedScanText($docIntelText, $scanType);
return $clean !== '' ? $clean : (preg_replace('/\s+/', ' ', trim($docIntelText)) ?: trim($docIntelText));
}
$azurePrompt = 'Read and extract all visible clinical text from this ' . strtoupper($scanType) . ' medical image. Return plain text only. If unclear, return best readable lines.';
$azureText = $this->callAzureOpenAiVision($filePath, $mimeType, $azurePrompt, 1800, false);
if ($azureText !== '') {
return preg_replace('/\s+/', ' ', trim($azureText)) ?: trim($azureText);
$clean = $this->sanitizeExtractedScanText($azureText, $scanType);
return $clean !== '' ? $clean : (preg_replace('/\s+/', ' ', trim($azureText)) ?: trim($azureText));
}
return null;
}
/**
* Clean OCR output so OPD UI stores readable clinical text only.
*/
private function sanitizeExtractedScanText(string $text, string $scanType): string
{
$text = trim($text);
if ($text === '') {
return '';
}
// Remove control and non-ASCII characters that usually come from OCR noise.
$text = preg_replace('/[^\x09\x0A\x0D\x20-\x7E]/', ' ', $text) ?? $text;
// Drop repeated-char junk words like "ffffff".
$text = preg_replace('/\b([A-Za-z])\1{4,}\b/', ' ', $text) ?? $text;
// Collapse very long repeated words into one token.
$text = preg_replace('/\b([A-Za-z]{2,})\b(?:\s+\1\b){2,}/i', '$1', $text) ?? $text;
$isEcg = strtolower(trim($scanType)) === 'ecg' || stripos($text, 'ecg') !== false;
if ($isEcg) {
// Common OCR fixes for ECG templates.
$replacements = [
'/\bKECG\b/i' => 'ECG',
'/\bUniconfirmed\b/i' => 'Unconfirmed',
'/\bavt\b/i' => 'aVL',
'/\baVR\b/i' => 'aVR',
'/\baVF\b/i' => 'aVF',
'/\baVL\b/i' => 'aVL',
'/\bVT\b/' => 'V1',
];
foreach ($replacements as $pattern => $replacement) {
$text = preg_replace($pattern, $replacement, $text) ?? $text;
}
}
// Normalize whitespace for compact storage/response.
$text = preg_replace('/\s+/', ' ', trim($text)) ?? trim($text);
return $text;
}
/**
* @return array{updated:bool,session_id:int,target_field:string}
*/