feat: add table detection options and scan artifact removal

- Add TableDetectionSelector component for wired/wireless/region detection
- Add CV-based table line detector module (disabled due to poor performance)
- Add scan artifact removal preprocessing step (removes faint horizontal lines)
- Add PreprocessingConfig schema with remove_scan_artifacts option
- Update frontend PreprocessingSettings with scan artifact toggle
- Integrate table detection config into ProcessingPage
- Archive extract-table-cell-boxes proposal

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
egg
2025-11-30 13:21:50 +08:00
parent f5a2c8a750
commit 95ae1f1bdb
17 changed files with 1906 additions and 344 deletions

View File

@@ -241,6 +241,25 @@ export default function PreprocessingSettings({
)}
</div>
{/* Scan Artifact Removal Toggle */}
<div className="space-y-2">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={config.remove_scan_artifacts}
onChange={(e) => handleConfigChange('remove_scan_artifacts', e.target.checked)}
disabled={disabled}
className="w-4 h-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<span className="text-sm text-gray-700">
{t('processing.preprocessing.removeScanArtifacts')}
</span>
</label>
<p className="text-xs text-gray-500 pl-6">
{t('processing.preprocessing.removeScanArtifactsDesc')}
</p>
</div>
{/* Binarize Toggle - Hidden by default, shown only in advanced mode */}
<details className="pt-2">
<summary className="text-xs text-gray-500 cursor-pointer hover:text-gray-700">

View File

@@ -0,0 +1,124 @@
import { cn } from '@/lib/utils'
import { Checkbox } from '@/components/ui/checkbox'
import { Table, Grid3X3, Rows3 } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import type { TableDetectionConfig } from '@/types/apiV2'
interface TableDetectionSelectorProps {
value: TableDetectionConfig
onChange: (config: TableDetectionConfig) => void
disabled?: boolean
className?: string
}
interface DetectionOption {
key: keyof TableDetectionConfig
icon: React.ReactNode
labelKey: string
descKey: string
}
const DETECTION_OPTIONS: DetectionOption[] = [
{
key: 'enable_wired_table',
icon: <Grid3X3 className="w-5 h-5" />,
labelKey: 'processing.tableDetection.wired',
descKey: 'processing.tableDetection.wiredDesc',
},
{
key: 'enable_wireless_table',
icon: <Rows3 className="w-5 h-5" />,
labelKey: 'processing.tableDetection.wireless',
descKey: 'processing.tableDetection.wirelessDesc',
},
{
key: 'enable_region_detection',
icon: <Table className="w-5 h-5" />,
labelKey: 'processing.tableDetection.region',
descKey: 'processing.tableDetection.regionDesc',
},
]
export default function TableDetectionSelector({
value,
onChange,
disabled = false,
className,
}: TableDetectionSelectorProps) {
const { t } = useTranslation()
const handleOptionChange = (key: keyof TableDetectionConfig, checked: boolean) => {
onChange({
...value,
[key]: checked,
})
}
return (
<div className={cn('border rounded-lg p-4 bg-white', className)}>
{/* Header */}
<div className="flex items-center gap-2 mb-4">
<Table className="w-5 h-5 text-gray-600" />
<h3 className="text-lg font-semibold text-gray-900">{t('processing.tableDetection.title')}</h3>
</div>
{/* Detection Options */}
<div className="space-y-3">
{DETECTION_OPTIONS.map((option) => {
const isChecked = value[option.key]
return (
<label
key={option.key}
className={cn(
'flex items-start gap-4 p-4 rounded-lg border-2 transition-all cursor-pointer',
isChecked
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 hover:border-gray-300 hover:bg-gray-50',
disabled && 'opacity-50 cursor-not-allowed'
)}
>
{/* Checkbox */}
<Checkbox
checked={isChecked}
onCheckedChange={(checked) => handleOptionChange(option.key, checked)}
disabled={disabled}
className="mt-0.5"
/>
{/* Icon */}
<div
className={cn(
'p-2 rounded-lg flex-shrink-0',
isChecked ? 'bg-blue-100 text-blue-600' : 'bg-gray-100 text-gray-500'
)}
>
{option.icon}
</div>
{/* Content */}
<div className="flex-1 min-w-0">
<span
className={cn(
'font-medium',
isChecked ? 'text-blue-700' : 'text-gray-900'
)}
>
{t(option.labelKey)}
</span>
<p className="text-sm text-gray-500 mt-1">{t(option.descKey)}</p>
</div>
</label>
)
})}
</div>
{/* Info Note */}
<div className="mt-4 p-3 bg-amber-50 border border-amber-200 rounded-md">
<p className="text-sm text-amber-800">
{t('processing.tableDetection.note')}
</p>
</div>
</div>
)
}

View File

@@ -64,6 +64,16 @@
"recommended": "推薦",
"note": "版面模型會影響文件結構(表格、文字區塊、圖片)的偵測效果。請根據您的文件類型選擇適合的模型。"
},
"tableDetection": {
"title": "表格偵測模式",
"wired": "有框線表格",
"wiredDesc": "偵測有明顯格線邊框的表格,適用於正式表格文件",
"wireless": "無框線表格",
"wirelessDesc": "偵測無邊框的表格,透過對齊方式推斷表格結構",
"region": "區域偵測",
"regionDesc": "輔助偵測表格區域,改善複雜表格的儲存格識別",
"note": "可同時啟用多種偵測模式,系統會自動整合偵測結果。如果表格儲存格框線不正確,請嘗試調整偵測模式。"
},
"preprocessing": {
"title": "影像前處理",
"mode": {
@@ -92,6 +102,8 @@
"strong": "強",
"maximum": "最強"
},
"removeScanArtifacts": "移除掃描瑕疵",
"removeScanArtifactsDesc": "移除掃描時光源產生的水平線痕,避免被誤判為表格框線",
"advanced": "進階選項",
"binarize": "二值化處理",
"binarizeWarning": "不建議使用",

View File

@@ -12,9 +12,10 @@ import { Play, CheckCircle, FileText, AlertCircle, Clock, Activity, Loader2, Inf
import LayoutModelSelector from '@/components/LayoutModelSelector'
import PreprocessingSettings from '@/components/PreprocessingSettings'
import PreprocessingPreview from '@/components/PreprocessingPreview'
import TableDetectionSelector from '@/components/TableDetectionSelector'
import TaskNotFound from '@/components/TaskNotFound'
import { useTaskValidation } from '@/hooks/useTaskValidation'
import type { LayoutModel, ProcessingOptions, PreprocessingMode, PreprocessingConfig, DocumentAnalysisResponse } from '@/types/apiV2'
import type { LayoutModel, ProcessingOptions, PreprocessingMode, PreprocessingConfig, TableDetectionConfig, DocumentAnalysisResponse } from '@/types/apiV2'
export default function ProcessingPage() {
const { t } = useTranslation()
@@ -44,9 +45,17 @@ export default function ProcessingPage() {
sharpen: true,
sharpen_strength: 1.0,
binarize: false,
remove_scan_artifacts: true,
})
const [showPreview, setShowPreview] = useState(false)
// Table detection state
const [tableDetectionConfig, setTableDetectionConfig] = useState<TableDetectionConfig>({
enable_wired_table: true,
enable_wireless_table: true,
enable_region_detection: true,
})
// Analyze document to determine if OCR is needed (only for pending tasks)
const { data: documentAnalysis, isLoading: isAnalyzing } = useQuery({
queryKey: ['documentAnalysis', taskId],
@@ -70,6 +79,7 @@ export default function ProcessingPage() {
layout_model: layoutModel,
preprocessing_mode: preprocessingMode,
preprocessing_config: preprocessingMode === 'manual' ? preprocessingConfig : undefined,
table_detection: tableDetectionConfig,
}
return apiClientV2.startTask(taskId!, options)
@@ -441,6 +451,13 @@ export default function ProcessingPage() {
disabled={processOCRMutation.isPending}
/>
{/* Table Detection Settings */}
<TableDetectionSelector
value={tableDetectionConfig}
onChange={setTableDetectionConfig}
disabled={processOCRMutation.isPending}
/>
{/* Preprocessing Settings */}
<PreprocessingSettings
mode={preprocessingMode}

View File

@@ -108,6 +108,20 @@ export interface PreprocessingConfig {
sharpen: boolean
sharpen_strength: number // 0.5-2.0, default 1.0
binarize: boolean
remove_scan_artifacts: boolean // Remove horizontal scan line artifacts
}
/**
* Table detection configuration for PP-StructureV3.
* Controls which table detection modes to enable.
* - enable_wired_table: Tables with visible cell borders/grid lines
* - enable_wireless_table: Tables without visible borders
* - enable_region_detection: Detect table-like regions for better cell structure
*/
export interface TableDetectionConfig {
enable_wired_table: boolean
enable_wireless_table: boolean
enable_region_detection: boolean
}
/**
@@ -147,6 +161,7 @@ export interface ProcessingOptions {
layout_model?: LayoutModel // Layout detection model selection (OCR track only)
preprocessing_mode?: PreprocessingMode // Preprocessing mode (OCR track only)
preprocessing_config?: PreprocessingConfig // Manual preprocessing config
table_detection?: TableDetectionConfig // Table detection options (OCR track only)
}
export interface TaskCreate {