Skip to content

Commit 078b61f

Browse files
Merge branch 'master' of github.com:ZoneMinder/zoneminder
2 parents 638369e + 7e22dae commit 078b61f

File tree

5 files changed

+191
-38
lines changed

5 files changed

+191
-38
lines changed

db/zm_create.sql.in

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -887,14 +887,14 @@ CREATE TABLE `ZonePresets` (
887887
`CheckMethod` enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs',
888888
`MinPixelThreshold` smallint(5) unsigned default NULL,
889889
`MaxPixelThreshold` smallint(5) unsigned default NULL,
890-
`MinAlarmPixels` int(10) unsigned default NULL,
891-
`MaxAlarmPixels` int(10) unsigned default NULL,
890+
`MinAlarmPixels` DECIMAL(10,2) unsigned default NULL,
891+
`MaxAlarmPixels` DECIMAL(10,2) unsigned default NULL,
892892
`FilterX` tinyint(3) unsigned default NULL,
893893
`FilterY` tinyint(3) unsigned default NULL,
894-
`MinFilterPixels` int(10) unsigned default NULL,
895-
`MaxFilterPixels` int(10) unsigned default NULL,
896-
`MinBlobPixels` int(10) unsigned default NULL,
897-
`MaxBlobPixels` int(10) unsigned default NULL,
894+
`MinFilterPixels` DECIMAL(10,2) unsigned default NULL,
895+
`MaxFilterPixels` DECIMAL(10,2) unsigned default NULL,
896+
`MinBlobPixels` DECIMAL(10,2) unsigned default NULL,
897+
`MaxBlobPixels` DECIMAL(10,2) unsigned default NULL,
898898
`MinBlobs` smallint(5) unsigned default NULL,
899899
`MaxBlobs` smallint(5) unsigned default NULL,
900900
`OverloadFrames` smallint(5) unsigned NOT NULL default '0',
@@ -921,14 +921,14 @@ CREATE TABLE `Zones` (
921921
`CheckMethod` enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs',
922922
`MinPixelThreshold` smallint(5) unsigned default NULL,
923923
`MaxPixelThreshold` smallint(5) unsigned default NULL,
924-
`MinAlarmPixels` int(10) unsigned default NULL,
925-
`MaxAlarmPixels` int(10) unsigned default NULL,
924+
`MinAlarmPixels` DECIMAL(10,2) unsigned default NULL,
925+
`MaxAlarmPixels` DECIMAL(10,2) unsigned default NULL,
926926
`FilterX` tinyint(3) unsigned default NULL,
927927
`FilterY` tinyint(3) unsigned default NULL,
928-
`MinFilterPixels` int(10) unsigned default NULL,
929-
`MaxFilterPixels` int(10) unsigned default NULL,
930-
`MinBlobPixels` int(10) unsigned default NULL,
931-
`MaxBlobPixels` int(10) unsigned default NULL,
928+
`MinFilterPixels` DECIMAL(10,2) unsigned default NULL,
929+
`MaxFilterPixels` DECIMAL(10,2) unsigned default NULL,
930+
`MinBlobPixels` DECIMAL(10,2) unsigned default NULL,
931+
`MaxBlobPixels` DECIMAL(10,2) unsigned default NULL,
932932
`MinBlobs` smallint(5) unsigned default NULL,
933933
`MaxBlobs` smallint(5) unsigned default NULL,
934934
`OverloadFrames` smallint(5) unsigned NOT NULL default '0',
@@ -1338,6 +1338,7 @@ CREATE TABLE `Notifications` (
13381338
`Interval` int unsigned NOT NULL DEFAULT 0,
13391339
`PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled',
13401340
`AppVersion` varchar(32) DEFAULT NULL,
1341+
`Profile` varchar(128) DEFAULT NULL,
13411342
`BadgeCount` int NOT NULL DEFAULT 0,
13421343
`LastNotifiedAt` datetime DEFAULT NULL,
13431344
`CreatedOn` datetime DEFAULT NULL,

db/zm_update-1.39.3.sql

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,123 @@
1+
2+
--
3+
-- Add Profile column to Notifications table
4+
--
5+
6+
SET @s = (SELECT IF(
7+
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
8+
WHERE TABLE_SCHEMA = DATABASE()
9+
AND TABLE_NAME = 'Notifications'
10+
AND COLUMN_NAME = 'Profile'
11+
) > 0,
12+
"SELECT 'Column Profile already exists in Notifications'",
13+
"ALTER TABLE `Notifications` ADD `Profile` varchar(128) DEFAULT NULL AFTER `AppVersion`"
14+
));
15+
PREPARE stmt FROM @s;
16+
EXECUTE stmt;
17+
DEALLOCATE PREPARE stmt;
18+
19+
-- Convert zone threshold fields (MinAlarmPixels, etc.) from pixel counts
20+
-- to percentages of zone area, matching the coordinate percentage migration
21+
-- done in zm_update-1.39.2.sql.
22+
--
23+
24+
-- First convert existing pixel count values to percentages WHILE columns
25+
-- are still INT (values 0-100 fit in INT; ALTER to DECIMAL would fail on
26+
-- large pixel counts that exceed DECIMAL(10,2) max of 99999.99).
27+
--
28+
-- Zone pixel area = (Zones.Area * Monitors.Width * Monitors.Height) / 10000
29+
-- where Zones.Area is in percentage-space (0-10000) from zm_update-1.39.2.sql.
30+
-- new_percent = old_pixel_count * 100 / zone_pixel_area
31+
-- = old_pixel_count * 1000000 / (Zones.Area * Monitors.Width * Monitors.Height)
32+
--
33+
-- Only convert zones with percentage coordinates (contain '.') that still have
34+
-- pixel-scale threshold values (> 100 means it can't be a percentage).
35+
36+
UPDATE Zones z
37+
JOIN Monitors m ON z.MonitorId = m.Id
38+
SET
39+
z.MinAlarmPixels = CASE
40+
WHEN z.MinAlarmPixels IS NULL THEN NULL
41+
WHEN z.MinAlarmPixels = 0 THEN 0
42+
WHEN z.Area > 0 AND m.Width > 0 AND m.Height > 0
43+
THEN LEAST(ROUND(z.MinAlarmPixels * 1000000.0 / (z.Area * m.Width * m.Height)), 100)
44+
ELSE z.MinAlarmPixels END,
45+
z.MaxAlarmPixels = CASE
46+
WHEN z.MaxAlarmPixels IS NULL THEN NULL
47+
WHEN z.MaxAlarmPixels = 0 THEN 0
48+
WHEN z.Area > 0 AND m.Width > 0 AND m.Height > 0
49+
THEN LEAST(ROUND(z.MaxAlarmPixels * 1000000.0 / (z.Area * m.Width * m.Height)), 100)
50+
ELSE z.MaxAlarmPixels END,
51+
z.MinFilterPixels = CASE
52+
WHEN z.MinFilterPixels IS NULL THEN NULL
53+
WHEN z.MinFilterPixels = 0 THEN 0
54+
WHEN z.Area > 0 AND m.Width > 0 AND m.Height > 0
55+
THEN LEAST(ROUND(z.MinFilterPixels * 1000000.0 / (z.Area * m.Width * m.Height)), 100)
56+
ELSE z.MinFilterPixels END,
57+
z.MaxFilterPixels = CASE
58+
WHEN z.MaxFilterPixels IS NULL THEN NULL
59+
WHEN z.MaxFilterPixels = 0 THEN 0
60+
WHEN z.Area > 0 AND m.Width > 0 AND m.Height > 0
61+
THEN LEAST(ROUND(z.MaxFilterPixels * 1000000.0 / (z.Area * m.Width * m.Height)), 100)
62+
ELSE z.MaxFilterPixels END,
63+
z.MinBlobPixels = CASE
64+
WHEN z.MinBlobPixels IS NULL THEN NULL
65+
WHEN z.MinBlobPixels = 0 THEN 0
66+
WHEN z.Area > 0 AND m.Width > 0 AND m.Height > 0
67+
THEN LEAST(ROUND(z.MinBlobPixels * 1000000.0 / (z.Area * m.Width * m.Height)), 100)
68+
ELSE z.MinBlobPixels END,
69+
z.MaxBlobPixels = CASE
70+
WHEN z.MaxBlobPixels IS NULL THEN NULL
71+
WHEN z.MaxBlobPixels = 0 THEN 0
72+
WHEN z.Area > 0 AND m.Width > 0 AND m.Height > 0
73+
THEN LEAST(ROUND(z.MaxBlobPixels * 1000000.0 / (z.Area * m.Width * m.Height)), 100)
74+
ELSE z.MaxBlobPixels END
75+
WHERE z.Coords LIKE '%.%'
76+
AND (z.MinAlarmPixels > 100 OR z.MaxAlarmPixels > 100
77+
OR z.MinFilterPixels > 100 OR z.MaxFilterPixels > 100
78+
OR z.MinBlobPixels > 100 OR z.MaxBlobPixels > 100);
79+
80+
-- Now change threshold columns from int to DECIMAL(10,2) to store percentages
81+
-- with 2 decimal places (e.g. 25.50 = 25.50% of zone area).
82+
-- Values are now 0-100 from the UPDATE above, so they fit in DECIMAL(10,2).
83+
84+
SET @s = (SELECT IF(
85+
(SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
86+
AND table_name = 'Zones' AND column_name = 'MinAlarmPixels'
87+
) = 'decimal',
88+
"SELECT 'Zones threshold columns already DECIMAL'",
89+
"ALTER TABLE `Zones`
90+
MODIFY `MinAlarmPixels` DECIMAL(10,2) unsigned default NULL,
91+
MODIFY `MaxAlarmPixels` DECIMAL(10,2) unsigned default NULL,
92+
MODIFY `MinFilterPixels` DECIMAL(10,2) unsigned default NULL,
93+
MODIFY `MaxFilterPixels` DECIMAL(10,2) unsigned default NULL,
94+
MODIFY `MinBlobPixels` DECIMAL(10,2) unsigned default NULL,
95+
MODIFY `MaxBlobPixels` DECIMAL(10,2) unsigned default NULL"
96+
));
97+
98+
PREPARE stmt FROM @s;
99+
EXECUTE stmt;
100+
DEALLOCATE PREPARE stmt;
101+
102+
-- Also update ZonePresets table column types for consistency
103+
SET @s = (SELECT IF(
104+
(SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
105+
AND table_name = 'ZonePresets' AND column_name = 'MinAlarmPixels'
106+
) = 'decimal',
107+
"SELECT 'ZonePresets threshold columns already DECIMAL'",
108+
"ALTER TABLE `ZonePresets`
109+
MODIFY `MinAlarmPixels` DECIMAL(10,2) unsigned default NULL,
110+
MODIFY `MaxAlarmPixels` DECIMAL(10,2) unsigned default NULL,
111+
MODIFY `MinFilterPixels` DECIMAL(10,2) unsigned default NULL,
112+
MODIFY `MaxFilterPixels` DECIMAL(10,2) unsigned default NULL,
113+
MODIFY `MinBlobPixels` DECIMAL(10,2) unsigned default NULL,
114+
MODIFY `MaxBlobPixels` DECIMAL(10,2) unsigned default NULL"
115+
));
116+
117+
PREPARE stmt FROM @s;
118+
EXECUTE stmt;
119+
DEALLOCATE PREPARE stmt;
120+
1121
--
2122
-- Add Menu_Items table for customizable navbar/sidebar menu
3123
--

src/zm_zone.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -956,21 +956,21 @@ std::vector<Zone> Zone::Load(const std::shared_ptr<Monitor> &monitor) {
956956
col++;
957957
int MaxPixelThreshold = dbrow[col]?atoi(dbrow[col]):0;
958958
col++;
959-
int MinAlarmPixels = dbrow[col]?atoi(dbrow[col]):0;
959+
double MinAlarmPixels_pct = dbrow[col] ? atof(dbrow[col]) : 0;
960960
col++;
961-
int MaxAlarmPixels = dbrow[col]?atoi(dbrow[col]):0;
961+
double MaxAlarmPixels_pct = dbrow[col] ? atof(dbrow[col]) : 0;
962962
col++;
963963
int FilterX = dbrow[col]?atoi(dbrow[col]):0;
964964
col++;
965965
int FilterY = dbrow[col]?atoi(dbrow[col]):0;
966966
col++;
967-
int MinFilterPixels = dbrow[col]?atoi(dbrow[col]):0;
967+
double MinFilterPixels_pct = dbrow[col] ? atof(dbrow[col]) : 0;
968968
col++;
969-
int MaxFilterPixels = dbrow[col]?atoi(dbrow[col]):0;
969+
double MaxFilterPixels_pct = dbrow[col] ? atof(dbrow[col]) : 0;
970970
col++;
971-
int MinBlobPixels = dbrow[col]?atoi(dbrow[col]):0;
971+
double MinBlobPixels_pct = dbrow[col] ? atof(dbrow[col]) : 0;
972972
col++;
973-
int MaxBlobPixels = dbrow[col]?atoi(dbrow[col]):0;
973+
double MaxBlobPixels_pct = dbrow[col] ? atof(dbrow[col]) : 0;
974974
col++;
975975
int MinBlobs = dbrow[col]?atoi(dbrow[col]):0;
976976
col++;
@@ -1004,6 +1004,27 @@ std::vector<Zone> Zone::Load(const std::shared_ptr<Monitor> &monitor) {
10041004
}
10051005
}
10061006

1007+
// Convert threshold values from DB format to pixel counts for runtime use.
1008+
// Percentage coordinates: thresholds are stored as % of zone area, convert to pixels.
1009+
// Legacy pixel coordinates: thresholds are already pixel counts.
1010+
int MinAlarmPixels, MaxAlarmPixels, MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels;
1011+
if (strchr(Coords, '.') && polygon.Area() > 0) {
1012+
int zpa = polygon.Area();
1013+
MinAlarmPixels = MinAlarmPixels_pct > 0 ? static_cast<int>(MinAlarmPixels_pct * zpa / 100.0 + 0.5) : 0;
1014+
MaxAlarmPixels = MaxAlarmPixels_pct > 0 ? static_cast<int>(MaxAlarmPixels_pct * zpa / 100.0 + 0.5) : 0;
1015+
MinFilterPixels = MinFilterPixels_pct > 0 ? static_cast<int>(MinFilterPixels_pct * zpa / 100.0 + 0.5) : 0;
1016+
MaxFilterPixels = MaxFilterPixels_pct > 0 ? static_cast<int>(MaxFilterPixels_pct * zpa / 100.0 + 0.5) : 0;
1017+
MinBlobPixels = MinBlobPixels_pct > 0 ? static_cast<int>(MinBlobPixels_pct * zpa / 100.0 + 0.5) : 0;
1018+
MaxBlobPixels = MaxBlobPixels_pct > 0 ? static_cast<int>(MaxBlobPixels_pct * zpa / 100.0 + 0.5) : 0;
1019+
} else {
1020+
MinAlarmPixels = static_cast<int>(MinAlarmPixels_pct);
1021+
MaxAlarmPixels = static_cast<int>(MaxAlarmPixels_pct);
1022+
MinFilterPixels = static_cast<int>(MinFilterPixels_pct);
1023+
MaxFilterPixels = static_cast<int>(MaxFilterPixels_pct);
1024+
MinBlobPixels = static_cast<int>(MinBlobPixels_pct);
1025+
MaxBlobPixels = static_cast<int>(MaxBlobPixels_pct);
1026+
}
1027+
10071028
if (atoi(dbrow[2]) == Zone::INACTIVE) {
10081029
zones.emplace_back(monitor, Id, Name, polygon);
10091030
} else if (atoi(dbrow[2]) == Zone::PRIVACY) {

web/includes/actions/zone.php

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,9 @@
3131
$zone = array();
3232
}
3333

34-
if ( $_REQUEST['newZone']['Units'] == 'Percent' ) {
35-
// Convert percentage thresholds to pixel counts using actual monitor pixel area
36-
$pixelArea = $monitor->ViewWidth() * $monitor->ViewHeight();
37-
foreach (array(
38-
'MinAlarmPixels','MaxAlarmPixels',
39-
'MinFilterPixels','MaxFilterPixels',
40-
'MinBlobPixels','MaxBlobPixels'
41-
) as $field ) {
42-
if ( isset($_REQUEST['newZone'][$field]) and $_REQUEST['newZone'][$field] )
43-
$_REQUEST['newZone'][$field] = intval(($_REQUEST['newZone'][$field]*$pixelArea)/100);
44-
}
45-
}
34+
// Threshold fields (MinAlarmPixels, etc.) are always submitted as percentages
35+
// of zone area by the JavaScript submitForm() function. If displaying in Pixels
36+
// mode, submitForm() converts back to percentages before submitting.
4637

4738
unset($_REQUEST['newZone']['Points']);
4839

web/skins/classic/views/js/zone.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ function submitForm(form) {
7171
form.elements['newZone[Coords]'].value = getCoordString();
7272
form.elements['newZone[Area]'].value = zone.Area;
7373

74+
// DB stores threshold values as percentages of zone area.
75+
// If displaying in Pixels mode, convert back to percentages before submitting.
76+
if (form.elements['newZone[Units]'].value == 'Pixels') {
77+
var pixelArea = Math.round(zone.Area / monitorArea * monitorPixelArea);
78+
toPercent(form.elements['newZone[MinAlarmPixels]'], pixelArea);
79+
toPercent(form.elements['newZone[MaxAlarmPixels]'], pixelArea);
80+
toPercent(form.elements['newZone[MinFilterPixels]'], pixelArea);
81+
toPercent(form.elements['newZone[MaxFilterPixels]'], pixelArea);
82+
toPercent(form.elements['newZone[MinBlobPixels]'], pixelArea);
83+
toPercent(form.elements['newZone[MaxBlobPixels]'], pixelArea);
84+
}
85+
7486
form.submit();
7587
}
7688

@@ -211,8 +223,8 @@ function toPercent(field, maxValue) {
211223

212224
function applyZoneUnits() {
213225
// zone.Area is in percentage-space (0-10000 for full frame)
214-
// Threshold fields are stored as pixel counts in the DB
215-
// Convert to pixel area for threshold display conversions
226+
// Threshold fields are stored as percentages of zone area in the DB
227+
// pixelArea is zone's actual pixel area, used for converting between display modes
216228
var pixelArea = Math.round(zone.Area / monitorArea * monitorPixelArea);
217229

218230
var form = document.zoneForm;
@@ -237,11 +249,11 @@ function applyZoneUnits() {
237249

238250
function limitRange(field, minValue, maxValue) {
239251
if ( field.value != '' ) {
240-
field.value = constrainValue(
241-
parseFloat(field.value),
242-
parseInt(minValue),
243-
parseInt(maxValue)
244-
);
252+
var currentValue = parseFloat(field.value);
253+
var constrainedValue = constrainValue(currentValue, parseInt(minValue), parseInt(maxValue));
254+
if ( constrainedValue !== currentValue ) {
255+
field.value = constrainedValue;
256+
}
245257
}
246258
}
247259

@@ -669,7 +681,15 @@ function initPage() {
669681
applyZoneType();
670682

671683
if ( form.elements['newZone[Units]'].value == 'Percent' ) {
672-
applyZoneUnits();
684+
// DB stores threshold values as percentages of zone area.
685+
// In Percent mode, values are already correct; just set Area display and field attributes.
686+
form.elements['newZone[Area]'].value = Math.round(zone.Area / monitorArea * 100);
687+
var thresholdFields = ['MinAlarmPixels', 'MaxAlarmPixels', 'MinFilterPixels', 'MaxFilterPixels', 'MinBlobPixels', 'MaxBlobPixels'];
688+
for (var i = 0; i < thresholdFields.length; i++) {
689+
var field = form.elements['newZone[' + thresholdFields[i] + ']'];
690+
field.setAttribute('step', 'any');
691+
field.setAttribute('max', 100);
692+
}
673693
}
674694

675695
applyCheckMethod();

0 commit comments

Comments
 (0)