Add save and display image before OCR

This commit is contained in:
Weitheng Haw 2025-02-04 03:21:24 +00:00
parent bd7c10f5f1
commit 2b1afe6073
3 changed files with 55 additions and 8 deletions

View File

@ -165,6 +165,14 @@ class LicensePlateRecognition:
logger.debug("Model runners not loaded")
return [], [], []
# Save raw image before processing if event_id is provided
if event_id:
try:
raw_filename = f"raw_{event_id}.jpg"
cv2.imwrite(os.path.join(self.debug_dir, raw_filename), image)
except Exception as e:
logger.warning(f"Failed to save raw debug image: {e}")
plate_points = self.detect(image)
if len(plate_points) == 0:
return [], [], []

View File

@ -32,7 +32,7 @@ import {
MobilePageTitle,
} from "@/components/mobile/MobilePage";
const LPR_TABS = ["details", "snapshot", "video", "plate"] as const;
const LPR_TABS = ["details", "snapshot", "video"] as const;
export type LPRTab = (typeof LPR_TABS)[number];
type LPRDetailDialogProps = {
@ -146,7 +146,6 @@ export default function LPRDetailDialog({
{item == "details" && <FaRegListAlt className="size-4" />}
{item == "snapshot" && <FaImage className="size-4" />}
{item == "video" && <FaVideo className="size-4" />}
{item == "plate" && <FaImage className="size-4" />}
<div className="capitalize">{item}</div>
</ToggleGroupItem>
))}
@ -163,7 +162,7 @@ export default function LPRDetailDialog({
{page === "video" && (
<VideoTab event={event} />
)}
{page === "plate" && (
{(page === "details" || !event) && (
<PlateTab lprImage={lprImage} />
)}
</Content>

View File

@ -10,7 +10,7 @@ import {
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ScrollArea } from "@/components/ui/scroll-area";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { Toaster } from "@/components/ui/sonner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { FrigateConfig } from "@/types/frigateConfig";
@ -21,13 +21,17 @@ import { useFormattedTimestamp } from "@/hooks/use-date-utils";
import { LuArrowDownUp, LuTrash2 } from "react-icons/lu";
import axios from "axios";
import { toast } from "sonner";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { cn } from "@/lib/utils";
type SortOption = "score_desc" | "score_asc" | "time_desc" | "time_asc";
type ViewMode = "detected" | "raw";
export default function LPRDebug() {
const { data: config } = useSWR<FrigateConfig>("config");
const [sortBy, setSortBy] = useState<SortOption>("time_desc");
const [selectedCameras, setSelectedCameras] = useState<string[] | undefined>();
const [viewMode, setViewMode] = useState<ViewMode>("detected");
// title
useEffect(() => {
@ -105,6 +109,35 @@ export default function LPRDebug() {
<div className="relative mb-2 flex h-11 w-full items-center justify-between">
<ScrollArea className="w-full whitespace-nowrap">
<div className="flex flex-row">
<ToggleGroup
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={viewMode}
onValueChange={(value: ViewMode) => {
if (value) {
setViewMode(value);
}
}}
>
<ToggleGroupItem
value="detected"
className={`flex scroll-mx-10 items-center justify-between gap-2 ${viewMode == "detected" ? "" : "*:text-muted-foreground"}`}
data-nav-item="detected"
aria-label="Select detected"
>
<div>Detected</div>
</ToggleGroupItem>
<ToggleGroupItem
value="raw"
className={`flex scroll-mx-10 items-center justify-between gap-2 ${viewMode == "raw" ? "" : "*:text-muted-foreground"}`}
data-nav-item="raw"
aria-label="Select raw"
>
<div>Raw</div>
</ToggleGroupItem>
</ToggleGroup>
<ScrollBar orientation="horizontal" className="h-0" />
</div>
</ScrollArea>
<div className="flex gap-2">
@ -141,7 +174,13 @@ export default function LPRDebug() {
</div>
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll">
{lprAttempts.map((attempt: string) => (
<LPRAttempt key={attempt} attempt={attempt} config={config} onRefresh={refreshLPR} />
<LPRAttempt
key={attempt}
attempt={attempt}
config={config}
onRefresh={refreshLPR}
viewMode={viewMode}
/>
))}
</div>
</div>
@ -152,9 +191,10 @@ type LPRAttemptProps = {
attempt: string;
config: FrigateConfig;
onRefresh: () => void;
viewMode: ViewMode;
};
function LPRAttempt({ attempt, config, onRefresh }: LPRAttemptProps) {
function LPRAttempt({ attempt, config, onRefresh, viewMode }: LPRAttemptProps) {
const [showDialog, setShowDialog] = useState(false);
const data = useMemo(() => {
const parts = attempt.split("_");
@ -206,7 +246,7 @@ function LPRAttempt({ attempt, config, onRefresh }: LPRAttemptProps) {
setOpen={setShowDialog}
event={event}
config={config}
lprImage={attempt}
lprImage={viewMode === "detected" ? attempt : `raw_${data.eventId}.jpg`}
/>
<div className="relative flex flex-col rounded-lg">
@ -217,7 +257,7 @@ function LPRAttempt({ attempt, config, onRefresh }: LPRAttemptProps) {
<div className="aspect-[2/1] flex items-center justify-center bg-black">
<img
className="h-40 max-w-none"
src={`${baseUrl}clips/lpr/${attempt}`}
src={`${baseUrl}clips/lpr/${viewMode === "detected" ? attempt : `raw_${data.eventId}.jpg`}`}
/>
</div>
</div>