mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-06 17:31:58 -04:00
Every query on Frames filters on EventId and orders or ranges by FrameId, and nothing needs a frame by Id. The Id primary key was a clustered index nothing read, and EventId_FrameId_idx was a secondary index every query used. On a copy of a local table that secondary index was half the table's size. With (EventId, FrameId) as the primary key the index is gone, and an event's rows are stored together, so deleting an event is a range delete. zm_update-1.39.36.sql: - converts AI_Detections.FrameId from Frames.Id to the per-event frame number, drops its foreign key to Frames and indexes (EventId, FrameId). A composite foreign key cannot replace it: ON DELETE SET NULL would have to null the NOT NULL EventId, and Frames rows are written in batches, so a detection can be recorded before its frame row. - removes duplicate (EventId, FrameId) rows, keeping the earliest. - rebuilds Frames with the new primary key. - removes ON UPDATE CURRENT_TIMESTAMP from Frames.TimeStamp. Any UPDATE of a frame row was overwriting its capture time. Each step checks the current schema first, so the migration can be re-run. REST API: view, edit and delete take /frames/<action>/<EventId>/<FrameId>.json. The old single-Id URLs return 404. CakePHP 2 has no composite keys, so the model's primaryKey is EventId. That keeps Event's dependent cascade delete limited to the event's own frames. The controller writes with explicit (EventId, FrameId) conditions instead of save(), which would match rows on EventId alone. Edit no longer changes EventId or FrameId. view=image with fid but no eid used to look up Frames.Id. It now returns 404. The Perl Frame class is identified by (EventId, FrameId). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
3.4 KiB
SQL
81 lines
3.4 KiB
SQL
--
|
|
-- Add AI Models tables for ZoneMinder
|
|
--
|
|
|
|
-- AI Datasets table - stores datasets like COCO, ImageNet, etc.
|
|
CREATE TABLE IF NOT EXISTS `AI_Datasets` (
|
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
|
`Name` varchar(64) NOT NULL,
|
|
`Description` TEXT,
|
|
`Version` varchar(32),
|
|
`NumClasses` int(10) unsigned NOT NULL,
|
|
PRIMARY KEY (`Id`),
|
|
UNIQUE KEY `AI_Datasets_Name_idx` (`Name`)
|
|
) ENGINE=InnoDB;
|
|
|
|
-- AI Models table - stores AI model implementations
|
|
CREATE TABLE IF NOT EXISTS `AI_Models` (
|
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
|
`Name` varchar(64) NOT NULL,
|
|
`Description` TEXT,
|
|
`ModelPath` varchar(255),
|
|
`Framework` enum('TensorFlow','PyTorch','ONNX','OpenVINO','TensorRT','Other') NOT NULL default 'ONNX',
|
|
`Version` varchar(32),
|
|
`DatasetId` int(10) unsigned,
|
|
`Enabled` tinyint(1) unsigned NOT NULL default 0,
|
|
PRIMARY KEY (`Id`),
|
|
UNIQUE KEY `AI_Models_Name_idx` (`Name`),
|
|
FOREIGN KEY (`DatasetId`) REFERENCES `AI_Datasets` (`Id`) ON DELETE SET NULL
|
|
) ENGINE=InnoDB;
|
|
|
|
-- AI Object Classes table - stores object classes from datasets
|
|
CREATE TABLE IF NOT EXISTS `AI_Object_Classes` (
|
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
|
`DatasetId` int(10) unsigned NOT NULL,
|
|
`ClassName` varchar(64) NOT NULL,
|
|
`ClassIndex` int(10) unsigned NOT NULL,
|
|
`Description` TEXT,
|
|
PRIMARY KEY (`Id`),
|
|
KEY `AI_Object_Classes_DatasetId_idx` (`DatasetId`),
|
|
UNIQUE KEY `AI_Object_Classes_Dataset_Class_idx` (`DatasetId`, `ClassName`),
|
|
FOREIGN KEY (`DatasetId`) REFERENCES `AI_Datasets` (`Id`) ON DELETE CASCADE
|
|
) ENGINE=InnoDB;
|
|
|
|
-- AI Detection Settings table - stores detection settings per monitor and object class
|
|
CREATE TABLE IF NOT EXISTS `AI_Detection_Settings` (
|
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
|
`MonitorId` int(10) unsigned NULL,
|
|
`ObjectClassId` int(10) unsigned NOT NULL,
|
|
`Enabled` tinyint(1) unsigned NOT NULL default 1,
|
|
`ReportDetection` tinyint(1) unsigned NOT NULL default 1,
|
|
`ConfidenceThreshold` tinyint(3) unsigned NOT NULL default 50,
|
|
`BoxColor` varchar(7) NOT NULL default '#FF0000',
|
|
PRIMARY KEY (`Id`),
|
|
KEY `AI_Detection_Settings_MonitorId_idx` (`MonitorId`),
|
|
KEY `AI_Detection_Settings_ObjectClassId_idx` (`ObjectClassId`),
|
|
UNIQUE KEY `AI_Detection_Settings_Monitor_Object_idx` (`MonitorId`, `ObjectClassId`),
|
|
FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE,
|
|
FOREIGN KEY (`ObjectClassId`) REFERENCES `AI_Object_Classes` (`Id`) ON DELETE CASCADE
|
|
) ENGINE=InnoDB;
|
|
|
|
-- AI Detections table - stores actual detection results
|
|
CREATE TABLE IF NOT EXISTS `AI_Detections` (
|
|
`Id` BIGINT unsigned NOT NULL auto_increment,
|
|
`EventId` BIGINT unsigned NOT NULL,
|
|
/* Frames.FrameId within EventId. Not a foreign key: Frames rows are
|
|
written in batches, so a detection can be recorded before its frame. */
|
|
`FrameId` int(10) unsigned,
|
|
`ObjectClassId` int(10) unsigned NOT NULL,
|
|
`Confidence` decimal(5,4) NOT NULL,
|
|
`BoundingBoxX` int(10) unsigned,
|
|
`BoundingBoxY` int(10) unsigned,
|
|
`BoundingBoxWidth` int(10) unsigned,
|
|
`BoundingBoxHeight` int(10) unsigned,
|
|
`DetectedAt` TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3),
|
|
PRIMARY KEY (`Id`),
|
|
KEY `AI_Detections_EventId_FrameId_idx` (`EventId`,`FrameId`),
|
|
KEY `AI_Detections_ObjectClassId_idx` (`ObjectClassId`),
|
|
FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE,
|
|
FOREIGN KEY (`ObjectClassId`) REFERENCES `AI_Object_Classes` (`Id`) ON DELETE CASCADE
|
|
) ENGINE=InnoDB;
|