feat: implement drain issue

This commit is contained in:
2026-03-19 13:12:02 +05:30
parent 8510bd9f32
commit 32b2c3c6ab
8 changed files with 122 additions and 90 deletions

View File

@@ -41,7 +41,13 @@ export default function VideoProcessingPage() {
setProgress(data.progress || 0);
let message = data.message || 'Processing...';
const uniqueCount =
data.unique_potholes ?? data.unique_signboards ?? data.unique_culverts;
data.unique_potholes ??
data.unique_pothole ??
data.unique_signboards ??
data.unique_signboard ??
data.unique_culverts ??
data.unique_culvert ??
data.unique_drain_issue;
if (uniqueCount !== undefined) {
message += ` | Unique: ${uniqueCount} | Total: ${data.total_detections || 0}`;
}

View File

@@ -5,6 +5,7 @@ import dynamic from 'next/dynamic';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Loader2, Milestone } from 'lucide-react';
import { Detection } from '@/types';
import { DETECTION_TYPES } from '@/constants/detectionModeConfig';
// Dynamically import the map to avoid SSR issues with Leaflet
const DashboardMapContent = dynamic(() => import('./dashboard-map-content'), {
@@ -78,20 +79,6 @@ export function DashboardMap({
// Filter detections with valid GPS coordinates
const validDetections = detections.filter((d) => d.latitude && d.longitude);
// Count detections by category
const counts = {
defected_sign_board: validDetections.filter(
(d) => d.type?.toLowerCase() === 'defected_sign_board',
).length,
pothole: validDetections.filter((d) => d.type?.toLowerCase() === 'pothole').length,
road_crack: validDetections.filter((d) => d.type?.toLowerCase() === 'road_crack').length,
damaged_road_marking: validDetections.filter(
(d) => d.type?.toLowerCase() === 'damaged_road_marking',
).length,
good_sign_board: validDetections.filter((d) => d.type?.toLowerCase() === 'good_sign_board')
.length,
};
return (
<Card className={`overflow-hidden ${className}`}>
<CardHeader className="pb-2">
@@ -110,36 +97,24 @@ export function DashboardMap({
</div>
</div>
{/* Compact Color Legend */}
{/* Dynamic Color Legend */}
<div className="flex flex-wrap items-center justify-end gap-x-4 gap-y-1 text-[10px] max-w-[60%]">
<div className="flex items-center gap-1">
<div className="w-2.5 h-2.5 rounded-full bg-red-500" />
<span className="text-muted-foreground font-medium">Potholes ({counts.pothole})</span>
</div>
<div className="flex items-center gap-1">
<div className="w-2.5 h-2.5 rounded-full bg-blue-500" />
{Object.values(DETECTION_TYPES).map((type) => {
const count = validDetections.filter(
(d) => (d.type || d.class || '').toLowerCase() === type.id.toLowerCase()
).length;
if (count === 0 && (type.id.includes('culvert') || type.id === 'drain_issue')) return null;
return (
<div key={type.id} className="flex items-center gap-1">
<div className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: type.color }} />
<span className="text-muted-foreground font-medium">
Defected Signs ({counts.defected_sign_board})
</span>
</div>
<div className="flex items-center gap-1">
<div className="w-2.5 h-2.5 rounded-full bg-amber-500" />
<span className="text-muted-foreground font-medium">
Cracks ({counts.road_crack})
</span>
</div>
<div className="flex items-center gap-1">
<div className="w-2.5 h-2.5 rounded-full bg-indigo-500" />
<span className="text-muted-foreground font-medium">
Markings ({counts.damaged_road_marking})
</span>
</div>
<div className="flex items-center gap-1">
<div className="w-2.5 h-2.5 rounded-full bg-emerald-500" />
<span className="text-muted-foreground font-medium">
Good Signs ({counts.good_sign_board})
{type.label} ({count})
</span>
</div>
);
})}
</div>
</div>
</CardHeader>

View File

@@ -4,6 +4,7 @@ import { Activity, Film } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { DetectionLogEntry } from '@/types';
import { DETECTION_TYPES } from '@/constants/detectionModeConfig';
interface DetectionLogsProps {
logs: DetectionLogEntry[];
@@ -41,11 +42,15 @@ const DetectionLogs = ({ logs, onSeek }: DetectionLogsProps) => {
<span className="text-xs text-muted-foreground/80">{log.videoTime}</span>
</div>
<div className="space-y-4">
{log.detections.map((d, j) => (
{log.detections.map((d, j) => {
const typeId = (d.type || '').toLowerCase();
const typeConfig = DETECTION_TYPES[typeId];
const label = typeConfig ? typeConfig.label : typeId.replace(/_/g, ' ');
return (
<div key={`${d.id}-${j}`} className="space-y-1">
<div className="text-[13px] font-bold">
{(d.type || '').replace(/ /g, '_')} ID: {d.id} | Confidence:{' '}
{(d.confidence * 100).toFixed(1)}%
{label} ID: {d.id} | Confidence: {(d.confidence * 100).toFixed(1)}%
</div>
{d.bbox && (
<div className="text-[12px] text-muted-foreground/80 font-medium">
@@ -60,7 +65,8 @@ const DetectionLogs = ({ logs, onSeek }: DetectionLogsProps) => {
</div>
)}
</div>
))}
);
})}
</div>
</div>
))

View File

@@ -58,6 +58,14 @@ export const DETECTION_TYPES: Record<string, DetectionTypeConfig> = {
listKey: 'good_sign_board_list',
frameCountKey: 'good_sign_board',
},
drain_issue: {
id: 'drain_issue',
label: 'Drain Issue',
color: '#a855f7',
countKey: 'unique_drain_issue',
listKey: 'drain_issue_list',
frameCountKey: 'drain_issue',
},
good_culvert: {
id: 'good_culvert',
label: 'Good Culvert',
@@ -77,10 +85,34 @@ export const DETECTION_TYPES: Record<string, DetectionTypeConfig> = {
};
export const DETECTION_MODES: Record<string, DetectionModeConfig> = {
yolo: {
id: 'yolo',
label: 'YOLO Detection',
enabledTypes: [
'pothole',
'defected_sign_board',
'road_crack',
'damaged_road_marking',
'good_sign_board',
'drain_issue',
],
},
yolo_vl: {
id: 'yolo_vl',
label: 'YOLO-VL Detection',
enabledTypes: [
'pothole',
'defected_sign_board',
'road_crack',
'damaged_road_marking',
'good_sign_board',
'drain_issue',
],
},
'pothole-detection': {
id: 'pothole-detection',
label: 'Pothole Detection',
enabledTypes: ['pothole', 'road_crack', 'damaged_road_marking'],
enabledTypes: ['pothole', 'road_crack', 'damaged_road_marking', 'drain_issue'],
},
'sign-board-detection': {
id: 'sign-board-detection',
@@ -96,6 +128,7 @@ export const DETECTION_MODES: Record<string, DetectionModeConfig> = {
'road_crack',
'damaged_road_marking',
'good_sign_board',
'drain_issue',
],
},
culvert_detection: {

View File

@@ -23,10 +23,8 @@ export const useFrameDetectionMap = (data: DetectionData, detectionType: string)
.map((d: any) => ({
...d,
_detType: (d.type || '').toLowerCase(),
// Map old ID fields for backward compatibility in UI components
pothole_id: d.type === 'pothole' ? d.detection_id : undefined,
signboard_id: d.type.includes('sign') ? d.detection_id : undefined,
culvert_id: d.type.includes('culvert') ? d.detection_id : undefined,
// Map old ID fields and new ID fields dynamically for backward compatibility
[`${(d.type || '').toLowerCase()}_id`]: d.detection_id,
}));
if (filteredDetections.length > 0) {
@@ -37,26 +35,30 @@ export const useFrameDetectionMap = (data: DetectionData, detectionType: string)
let detections: any[] = [];
enabledTypes.forEach((type) => {
const listKey =
type.id === 'pothole'
? 'potholes'
: type.id.includes('sign')
? 'signboards'
: type.id === 'good_culvert'
? 'good_culverts'
: type.id === 'defective_culvert'
? 'defective_culverts'
: null;
// Try common plural naming conventions for legacy support
const possibleKeys = [
`${type.id}s`,
type.id.endsWith('y') ? `${type.id.slice(0, -1)}ies` : `${type.id}s`,
type.listKey.replace('_list', 's'),
type.listKey,
];
if (listKey && (frameData as any)[listKey]) {
for (const listKey of possibleKeys) {
if ((frameData as any)[listKey]) {
detections = [
...detections,
...(frameData as any)[listKey].map((item: any) => ({
...item,
_detType: type.id,
type: type.id,
[`${type.id.toLowerCase()}_id`]:
(item as any).detection_id ??
(item as any)[`${type.id.toLowerCase()}_id`] ??
item.id,
})),
];
break;
}
}
});

View File

@@ -19,6 +19,7 @@ export type DetectionData = {
unique_road_crack?: number;
unique_damaged_road_marking?: number;
unique_good_sign_board?: number;
unique_drain_issue?: number;
unique_good_culvert?: number;
unique_defective_culvert?: number;
total_road_damage?: number;
@@ -31,6 +32,7 @@ export type DetectionData = {
road_crack_list?: Array<DetectionListItem>;
damaged_road_marking_list?: Array<DetectionListItem>;
good_sign_board_list?: Array<DetectionListItem>;
drain_issue_list?: Array<DetectionListItem>;
good_culvert_list?: Array<DetectionListItem>;
defective_culvert_list?: Array<DetectionListItem>;
signboard_list?: Array<DetectionListItem>; // Keeping for backward compatibility
@@ -64,6 +66,7 @@ export type DetectionData = {
road_crack: number;
damaged_road_marking: number;
good_sign_board: number;
drain_issue: number;
};
}>;
}>;

View File

@@ -17,7 +17,12 @@ export type DetectionType =
| 'pothole-detection'
| 'sign-board-detection'
| 'pot-sign-detection'
| 'culvert_detection';
| 'culvert_detection'
| 'yolo'
| 'yolo_vl'
| 'sam3'
| 'yoloe'
| 'yoloe_trained_vl';
export interface DetectionListItem {
detection_id?: number;
@@ -38,6 +43,7 @@ export interface DetectionCounts {
road_crack: number;
damaged_road_marking: number;
good_sign_board: number;
drain_issue: number;
good_culvert?: number;
defective_culvert?: number;
}

View File

@@ -11,6 +11,7 @@ export interface Video {
unique_road_crack?: number;
unique_damaged_road_marking?: number;
unique_good_sign_board?: number;
unique_drain_issue?: number;
total_road_damage?: number;
total_detections?: number;
created_at: string;