mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-11-08 21:53:07 -05:00
Add the schema backing per-monitor object detection and the AI dataset/ model/class management UI: - Monitors gains AnalysisImageOpacity and ObjectDetection, ObjectDetectionModel, ObjectDetectionObjectThreshold, ObjectDetectionNMSThreshold columns. - New tables AI_Datasets, AI_Models, AI_Object_Classes, AI_Detection_Settings and AI_Detections. - Seed the COCO 2017 dataset (80 classes) and default per-class detection settings via db/coco_dataset.sql. Existing installs get zm_update-1.39.17.sql, which is idempotent and adds the columns with their final VARCHAR(16) ObjectDetection shape directly (no enum-churn intermediates). Fresh installs create the same objects from zm_create.sql.in sourcing AI_Models.sql and coco_dataset.sql. Both paths were verified to produce identical schema. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
252 lines
9.9 KiB
SQL
252 lines
9.9 KiB
SQL
--
|
|
-- AI object-detection data model.
|
|
--
|
|
-- Adds the per-monitor object-detection columns and the AI_* tables used by
|
|
-- the object-detection management UI, then seeds the COCO 2017 dataset.
|
|
--
|
|
-- All statements are idempotent so re-running (or running after a fresh
|
|
-- install that already has these objects) is a no-op.
|
|
--
|
|
|
|
--
|
|
-- Monitors columns
|
|
--
|
|
|
|
-- AnalysisImageOpacity: alpha applied when compositing the analysis/annotated
|
|
-- image over the source frame (0-255).
|
|
SET @s = (SELECT IF(
|
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
|
AND table_name = 'Monitors' AND column_name = 'AnalysisImageOpacity') > 0,
|
|
"SELECT 'Column AnalysisImageOpacity already exists in Monitors'",
|
|
"ALTER TABLE `Monitors` ADD COLUMN `AnalysisImageOpacity` TINYINT UNSIGNED NOT NULL DEFAULT '128' AFTER `AnalysisImage`"
|
|
));
|
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
|
|
-- ObjectDetection: name of the detection backend for this monitor, or 'none'.
|
|
-- VARCHAR(16) so backends can be added without a schema change.
|
|
SET @s = (SELECT IF(
|
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
|
AND table_name = 'Monitors' AND column_name = 'ObjectDetection') > 0,
|
|
"SELECT 'Column ObjectDetection already exists in Monitors'",
|
|
"ALTER TABLE `Monitors` ADD COLUMN `ObjectDetection` VARCHAR(16) NOT NULL DEFAULT 'none' AFTER `AnalysisImageOpacity`"
|
|
));
|
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
|
|
-- ObjectDetectionModel: path/name of the model file used by the backend.
|
|
SET @s = (SELECT IF(
|
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
|
AND table_name = 'Monitors' AND column_name = 'ObjectDetectionModel') > 0,
|
|
"SELECT 'Column ObjectDetectionModel already exists in Monitors'",
|
|
"ALTER TABLE `Monitors` ADD COLUMN `ObjectDetectionModel` VARCHAR(255) NOT NULL DEFAULT '' AFTER `ObjectDetection`"
|
|
));
|
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
|
|
-- ObjectDetectionObjectThreshold: minimum confidence for a detection to count.
|
|
SET @s = (SELECT IF(
|
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
|
AND table_name = 'Monitors' AND column_name = 'ObjectDetectionObjectThreshold') > 0,
|
|
"SELECT 'Column ObjectDetectionObjectThreshold already exists in Monitors'",
|
|
"ALTER TABLE `Monitors` ADD COLUMN `ObjectDetectionObjectThreshold` FLOAT NOT NULL DEFAULT 0.4 AFTER `ObjectDetectionModel`"
|
|
));
|
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
|
|
-- ObjectDetectionNMSThreshold: non-maximum-suppression IoU threshold.
|
|
SET @s = (SELECT IF(
|
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
|
AND table_name = 'Monitors' AND column_name = 'ObjectDetectionNMSThreshold') > 0,
|
|
"SELECT 'Column ObjectDetectionNMSThreshold already exists in Monitors'",
|
|
"ALTER TABLE `Monitors` ADD COLUMN `ObjectDetectionNMSThreshold` FLOAT NOT NULL DEFAULT 0.25 AFTER `ObjectDetectionObjectThreshold`"
|
|
));
|
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
|
|
-- Widen ObjectDetection to VARCHAR(16) in case an older upgrade path left it as
|
|
-- an ENUM or a narrower VARCHAR.
|
|
ALTER TABLE `Monitors` MODIFY `ObjectDetection` VARCHAR(16) NOT NULL DEFAULT 'none';
|
|
|
|
--
|
|
-- AI tables
|
|
--
|
|
|
|
-- 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,
|
|
`FrameId` BIGINT 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_idx` (`EventId`),
|
|
KEY `AI_Detections_FrameId_idx` (`FrameId`),
|
|
KEY `AI_Detections_ObjectClassId_idx` (`ObjectClassId`),
|
|
FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE,
|
|
FOREIGN KEY (`FrameId`) REFERENCES `Frames` (`Id`) ON DELETE SET NULL,
|
|
FOREIGN KEY (`ObjectClassId`) REFERENCES `AI_Object_Classes` (`Id`) ON DELETE CASCADE
|
|
) ENGINE=InnoDB;
|
|
|
|
--
|
|
-- Seed the COCO 2017 dataset (80 classes) and default detection settings.
|
|
--
|
|
|
|
INSERT IGNORE INTO AI_Datasets (Id, Name, Description, Version, NumClasses) VALUES
|
|
(1, 'COCO', 'Microsoft Common Objects in Context', '2017', 80);
|
|
|
|
INSERT IGNORE INTO AI_Object_Classes (DatasetId, ClassName, ClassIndex, Description) VALUES
|
|
(1, 'person', 0, 'Person'),
|
|
(1, 'bicycle', 1, 'Bicycle'),
|
|
(1, 'car', 2, 'Car'),
|
|
(1, 'motorcycle', 3, 'Motorcycle'),
|
|
(1, 'airplane', 4, 'Airplane'),
|
|
(1, 'bus', 5, 'Bus'),
|
|
(1, 'train', 6, 'Train'),
|
|
(1, 'truck', 7, 'Truck'),
|
|
(1, 'boat', 8, 'Boat'),
|
|
(1, 'traffic light', 9, 'Traffic light'),
|
|
(1, 'fire hydrant', 10, 'Fire hydrant'),
|
|
(1, 'stop sign', 11, 'Stop sign'),
|
|
(1, 'parking meter', 12, 'Parking meter'),
|
|
(1, 'bench', 13, 'Bench'),
|
|
(1, 'bird', 14, 'Bird'),
|
|
(1, 'cat', 15, 'Cat'),
|
|
(1, 'dog', 16, 'Dog'),
|
|
(1, 'horse', 17, 'Horse'),
|
|
(1, 'sheep', 18, 'Sheep'),
|
|
(1, 'cow', 19, 'Cow'),
|
|
(1, 'elephant', 20, 'Elephant'),
|
|
(1, 'bear', 21, 'Bear'),
|
|
(1, 'zebra', 22, 'Zebra'),
|
|
(1, 'giraffe', 23, 'Giraffe'),
|
|
(1, 'backpack', 24, 'Backpack'),
|
|
(1, 'umbrella', 25, 'Umbrella'),
|
|
(1, 'handbag', 26, 'Handbag'),
|
|
(1, 'tie', 27, 'Tie'),
|
|
(1, 'suitcase', 28, 'Suitcase'),
|
|
(1, 'frisbee', 29, 'Frisbee'),
|
|
(1, 'skis', 30, 'Skis'),
|
|
(1, 'snowboard', 31, 'Snowboard'),
|
|
(1, 'sports ball', 32, 'Sports ball'),
|
|
(1, 'kite', 33, 'Kite'),
|
|
(1, 'baseball bat', 34, 'Baseball bat'),
|
|
(1, 'baseball glove', 35, 'Baseball glove'),
|
|
(1, 'skateboard', 36, 'Skateboard'),
|
|
(1, 'surfboard', 37, 'Surfboard'),
|
|
(1, 'tennis racket', 38, 'Tennis racket'),
|
|
(1, 'bottle', 39, 'Bottle'),
|
|
(1, 'wine glass', 40, 'Wine glass'),
|
|
(1, 'cup', 41, 'Cup'),
|
|
(1, 'fork', 42, 'Fork'),
|
|
(1, 'knife', 43, 'Knife'),
|
|
(1, 'spoon', 44, 'Spoon'),
|
|
(1, 'bowl', 45, 'Bowl'),
|
|
(1, 'banana', 46, 'Banana'),
|
|
(1, 'apple', 47, 'Apple'),
|
|
(1, 'sandwich', 48, 'Sandwich'),
|
|
(1, 'orange', 49, 'Orange'),
|
|
(1, 'broccoli', 50, 'Broccoli'),
|
|
(1, 'carrot', 51, 'Carrot'),
|
|
(1, 'hot dog', 52, 'Hot dog'),
|
|
(1, 'pizza', 53, 'Pizza'),
|
|
(1, 'donut', 54, 'Donut'),
|
|
(1, 'cake', 55, 'Cake'),
|
|
(1, 'chair', 56, 'Chair'),
|
|
(1, 'couch', 57, 'Couch'),
|
|
(1, 'potted plant', 58, 'Potted plant'),
|
|
(1, 'bed', 59, 'Bed'),
|
|
(1, 'dining table', 60, 'Dining table'),
|
|
(1, 'toilet', 61, 'Toilet'),
|
|
(1, 'tv', 62, 'TV'),
|
|
(1, 'laptop', 63, 'Laptop'),
|
|
(1, 'mouse', 64, 'Mouse'),
|
|
(1, 'remote', 65, 'Remote'),
|
|
(1, 'keyboard', 66, 'Keyboard'),
|
|
(1, 'cell phone', 67, 'Cell phone'),
|
|
(1, 'microwave', 68, 'Microwave'),
|
|
(1, 'oven', 69, 'Oven'),
|
|
(1, 'toaster', 70, 'Toaster'),
|
|
(1, 'sink', 71, 'Sink'),
|
|
(1, 'refrigerator', 72, 'Refrigerator'),
|
|
(1, 'book', 73, 'Book'),
|
|
(1, 'clock', 74, 'Clock'),
|
|
(1, 'vase', 75, 'Vase'),
|
|
(1, 'scissors', 76, 'Scissors'),
|
|
(1, 'teddy bear', 77, 'Teddy bear'),
|
|
(1, 'hair drier', 78, 'Hair drier'),
|
|
(1, 'toothbrush', 79, 'Toothbrush');
|
|
|
|
-- Default detection settings (MonitorId NULL = global default): enable person
|
|
-- and common vehicles.
|
|
INSERT IGNORE INTO AI_Detection_Settings (MonitorId, ObjectClassId, Enabled, ReportDetection, ConfidenceThreshold, BoxColor)
|
|
SELECT NULL, Id, 1, 1,
|
|
CASE WHEN ClassName = 'person' THEN 60 ELSE 50 END,
|
|
CASE
|
|
WHEN ClassName = 'person' THEN '#FF0000'
|
|
WHEN ClassName = 'car' THEN '#0000FF'
|
|
WHEN ClassName = 'truck' THEN '#0066FF'
|
|
WHEN ClassName = 'bus' THEN '#0099FF'
|
|
WHEN ClassName = 'motorcycle' THEN '#00CCFF'
|
|
ELSE '#808080'
|
|
END
|
|
FROM AI_Object_Classes
|
|
WHERE DatasetId = (SELECT Id FROM AI_Datasets WHERE Name = 'COCO' LIMIT 1)
|
|
AND ClassName IN ('person', 'car', 'truck', 'bus', 'motorcycle');
|