Commit Graph

187 Commits

Author SHA1 Message Date
Isaac Connor
e1eb7876c6 fix: clamp percentage polygon to width-1/height-1
ParsePercentagePolygon clamped to [0, width] / [0, height], so a new
zone created at 100% produced pixel coords equal to the monitor
dimensions. The rasterizer requires hi_x < width and hi_y < height,
so the runtime then logged warnings like
  "polygon hi_x (1920) >= image width (1920), clamping".

Clamp to width-1 / height-1 instead. Updates existing tests that
encoded the previous behavior.
2026-05-13 11:38:42 -04:00
Isaac Connor
29d371313a fix: store zone thresholds as percentages and convert to pixels at runtime
Zone threshold fields (MinAlarmPixels, MaxAlarmPixels, MinFilterPixels,
MaxFilterPixels, MinBlobPixels, MaxBlobPixels) were being corrupted on
every save because the PHP conversion used monitor pixel area instead of
zone pixel area. This caused values to inflate progressively, breaking
motion detection.

The fix changes the storage model: thresholds are now stored as
percentages of zone area (DECIMAL(7,2) columns) matching the percentage
coordinate system from zm_update-1.39.2. The C++ Zone::Load() converts
percentages to pixel counts at runtime using polygon.Area(). Legacy
pixel-coordinate zones pass through unchanged.

JS changes:
- submitForm() converts to percentages when in Pixels display mode
- Init skips applyZoneUnits() since DB values are already percentages
- limitRange() only updates field.value when constrained value differs,
  preventing oninput from stripping decimal points mid-keystroke

fixes #4690

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:16:56 -04:00
Isaac Connor
7d78b722d0 fix: auto-detect zone coordinate format instead of trusting Units field
The zone loader now ignores the Units DB field and detects the coordinate
format by checking for decimal points: decimal values are percentages,
integer-only values are legacy pixels. This fixes motion detection being
broken when zones had Units=Pixels but percentage coordinates (or vice
versa), which resulted in a ~99x99 pixel zone on a 2560x1440 monitor.

The PHP zone view now always forces Units=Percent when saving, since it
always works in percentage space. convertPixelPointsToPercent() now
returns bool to indicate whether conversion occurred.

Tests added for: truncation bug via atoi, correct percentage-to-pixel
conversion, auto-detect heuristic, and resolution independence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:26:36 -04:00
Isaac Connor
e8b5c4962e Convert warning to debug 2026-02-23 18:16:56 -05:00
Isaac Connor
47edcca6ab fix: fall back to pixel parsing when zone Units=Percent but coords exceed 100
When zones have Units='Percent' in the database but their Coords contain
pixel values (>100), ParsePercentagePolygon treats them as percentages,
causing wild scaling (e.g., 639% * 1920 / 100 = 12269) followed by
clamping to monitor bounds, producing degenerate full-frame zones.

Add a pre-check in Zone::Load that scans coordinate values before calling
ParsePercentagePolygon. If any value exceeds 100, log a warning and use
ParsePolygonString (pixel path) instead. Also add unit tests for both
ParsePolygonString and ParsePercentagePolygon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:16:55 -05:00
Isaac Connor
995b60db80 fix: use int instead of unsigned int for zone pixel coordinates in zm_zone.cpp
The lo_x/lo_y/hi_x/hi_y variables were unsigned int despite being assigned
from signed int32 Vector2 members and compared against signed int dimensions.
This caused scattered (int) and (unsigned int) casts throughout CheckAlarms,
std_alarmedpixels, and Setup. Switching to int eliminates the casts, fixes
the signed/unsigned comparison warnings, and makes the -1 sentinel check in
std_alarmedpixels explicit rather than relying on unsigned wraparound.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:16:55 -05:00
Isaac Connor
2013339b1e fix: clamp zone polygon extents to actual frame dimensions in CheckAlarms
When a camera reconnects at a different resolution than the zone polygons
were configured for, the polygon bounding box can exceed the frame
dimensions. This caused a heap buffer overflow in the memset that zeros
the bbox rows of the diff image, confirmed by Valgrind (invalid write
at 0 bytes past a 921600-byte / 1280x720 block).

- Validate delta_image buffer and dimensions at entry
- Clamp hi_y/hi_x to image height/width before memset and pixel loops
- Guard filter, blob, and HighlightEdges loops against empty rows where
  ranges[y].lo_x is -1 (would wrap to UINT_MAX as unsigned)
- Clamp extents in std_alarmedpixels independently for defense in depth

Warning() logs identify which zones need polygon reconfiguration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:16:55 -05:00
Isaac Connor
3e067f18b2 feat: store zone coordinates as percentages for resolution independence (src only)
Cherry-picked src/ changes from edge branch commit 17450d122.
Adds ParsePercentagePolygon() to convert percentage-based zone coordinates
to pixel values using monitor dimensions. Zone::Load() now checks the Units
column to dispatch between legacy pixel parsing and percentage-based parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:06:56 -05:00
Isaac Connor
31aa65bd11 perf: split delta/mask buffers in CheckAlarms to eliminate full-image copy
Instead of copying the entire delta_image into the per-zone mask buffer
every frame (4MB memcpy for 4MP), allocate a persistent grayscale mask
and only zero the polygon bounding-box rows.  alarmedpixels_row now
reads from delta_image (const, read-only) and writes threshold results
to the separate mask buffer.

Key changes:
- alarmedpixels_row takes separate pdelta (read) and pmask (write) pointers
- std_alarmedpixels accepts both delta_image (const) and mask_image
- CheckAlarms allocates mask with explicit linesize == width to match
  filter/blob pointer arithmetic (avoids FFALIGN padding mismatch)
- Full buffer zero on allocation; bbox-only zero on reuse
- No functional change to filter, blob, or HighlightEdges stages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:41:50 -05:00
Isaac Connor
8b3ed97fa4 perf: auto-vectorize alarmedpixels loop for SIMD on all platforms
Restructure std_alarmedpixels inner loop so GCC/Clang can auto-vectorize
it at -O2/-O3. The compiler now emits 16-byte SIMD (SSE2/NEON) processing
16 pixels per iteration instead of 1.

Three changes enable this:
- Extract inner loop into static alarmedpixels_row() with __restrict__
  on function parameters, giving the compiler a strong no-alias guarantee
- Use branchless bitwise AND instead of short-circuit && to avoid
  branches that block vectorization
- Remove per-row Debug(7) call that clobbered memory from the compiler's
  perspective, invalidating pointer analysis

The original hand-written SSE2 ASM (removed in 2011, commit 8e9ccfe1e)
had alignment restrictions and didn't use per-row polygon ranges. This
approach is portable, maintainable, and achieves equivalent throughput.

GCC confirms: "loop vectorized using 16 byte vectors"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:36:13 -05:00
Nic Boet
5fde4e5deb perf: reuse diff image buffer in Zone::CheckAlarms
Replace delete/new Image cycle with lazy-alloc + Assign(). When the
buffer already exists (every frame after the first), Assign() detects
matching dimensions and does a plain memcpy into the existing
allocation, eliminating an aligned malloc+free of ~2 MB per zone per
analyzed frame.

With 4 zones at 15 fps this removes 60 alloc/free cycles per second
from the analysis hot path. The HighlightEdges code path (analysis
images) still allocates a new Image and deletes the old diff buffer,
which is correct — the next Assign() will reallocate once to restore
the single-channel format, then resume reuse.

Behaviorally equivalent: Zone dimensions are constant during zone
lifetime, the destructor already handles cleanup via delete image,
and the only external consumer (Monitor::Analyse → AlarmImage →
Overlay) reads the image without storing pointers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:25:58 -06:00
Isaac Connor
a96b776949 fix: apply credentials to secondary URL and fix buffer overflow in DumpSettings
- Apply credentials to secondary stream URL in FFmpegCamera (was causing 401 Unauthorized)
- Add empty check for rtsp_second_path in RTSP2WebManager before applying credentials
- Replace unsafe sprintf pattern in Monitor::DumpSettings with std::string + stringtf
- Refactor Zone::DumpSettings to return std::string instead of writing to char buffer
- Add decimal precision to event duration debug output

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:40:26 -05:00
Steve Gilvarry
1b527c1b62 Remove unused row index in zone load loop 2025-12-31 22:18:32 +11:00
Aaron Kling
c4683d90a9 Format code using astyle google format
Commands used:
astyle --style=google --indent=spaces=2 --keep-one-line-blocks src/*.cpp
astyle --style=google --indent=spaces=2 --keep-one-line-blocks src/*.h
2024-03-26 13:43:58 -05:00
Isaac Connor
4993a55b14 Move declaration of zones down to where it is used 2024-02-07 15:56:24 -05:00
Isaac Connor
d0ca677207 Fix %u instead of %d for id which is unsigned. Pass string instead of c_str 2022-07-14 12:07:26 -04:00
Isaac Connor
9093a8694b Fix crashes. Zones should use const for monitor object and not references. 2022-06-07 20:52:39 -04:00
Isaac Connor
07e32025df Just store the linked_monitors string instead of parsing and loading of related objects. Only call ReloadLinkedMonitors if doing analysis. Convert zone id's to unsigned 2022-06-07 18:36:25 -04:00
Isaac Connor
78a87517f6 convert to taking a std::shared_ptr<Monitor> instead of a Monitor *. Implement Name() which just returns the std::string label. 2022-06-07 18:18:24 -04:00
Isaac Connor
e51d3a206f Use auto to get the right type for n_coords 2022-05-18 17:07:06 -04:00
Isaac Connor
b254f990d0 Warn when the number of vertices changes due to clipping 2022-05-17 15:05:25 -04:00
Isaac Connor
3f7b9dfff7 fix cppcheck error about lo_x. Inner scope overwriting it. 2022-01-10 17:36:50 -05:00
Isaac Connor
e8bb095730 include monitor dimensions when logging about zone mismatch 2021-11-24 14:28:31 -05:00
Mike Dussault
d38a6adec4 No behavior change. Added non-const versions of Image::Buffer and fixed a few places that were casting away the constness. 2021-10-12 21:54:49 +00:00
Isaac Connor
b0cf3a4732 Merge pull request #3314 from Carbenium/path-max
Fix Wformat for stringtf and convert path buffers depending on PATH_MAX to std::string
2021-07-07 11:34:03 -04:00
Peter Keresztes Schmidt
80b08a2075 Convert path buffers depending on PATH_MAX to std::string 2021-07-06 10:33:17 +02:00
Peter Keresztes Schmidt
65656de6ce db: Adjust the query methods to accept std::strings 2021-07-06 10:20:46 +02:00
Peter Keresztes Schmidt
c60b577aec Convert more char array buffers to std::string
Remove now unused ZM_SQL_*SIZE defines
2021-07-06 10:20:46 +02:00
Peter Keresztes Schmidt
b1de220958 Polygon: Perform clip operation on existing object instead of returning a new clipped one 2021-05-16 19:42:41 +02:00
Peter Keresztes Schmidt
29488900a1 Box: Make range calculations mathematically correct 2021-05-16 19:41:45 +02:00
Peter Keresztes Schmidt
ef7a083891 Zone: Actually clip the zone if it larger than the image
Until now only the boundary box was manually adjusted without actually clipping the polygon.
2021-05-16 19:41:45 +02:00
Peter Keresztes Schmidt
5af6d6af3d Polygon: Use std::vector to store the vertices 2021-05-16 16:42:58 +02:00
Peter Keresztes Schmidt
8f685b3d66 Box+Poly: Remove direct accessors to {Hi,Lo}{X,Y} 2021-05-16 16:42:58 +02:00
Peter Keresztes Schmidt
e6c159fb70 Vector2: Make coordinate components public
The components were already unconditionally/without side-effects writable. Let's make them public so we don't need the setters.
2021-05-16 16:42:58 +02:00
Peter Keresztes Schmidt
60db1c2eaf Coord: Rename to Vector2
The class is not only used to represent coordinates but also lengths in XY.
Vector2 is a more fitting/general name for this purpose.
2021-05-14 20:14:50 +02:00
Isaac Connor
2cf6ad8089 Switch ZMPacket * to a shared_ptr<ZMPacket>. This is so that in LockedPacket we can unlock and then notify and be confident that packet_ won't have been deleted. Change ZMPacket->timestamp to be a timeval instead of timeval *. This might not have been necessary but I like it. No longer cuse the ZMPacket object to wrap the shared image buffers and timestamps. Use a vector for image_buffers. 2021-05-08 21:14:20 -04:00
Isaac Connor
a91197b47c reset score_ to zero when we return for failing to meet pixel filters 2021-05-07 14:23:36 -04:00
Peter Keresztes Schmidt
4180bc99ac Zone: Sprinkle some const-ness
Keep references in for-loops const if no modifications are preformed on the object.
2021-04-25 22:34:33 +02:00
Peter Keresztes Schmidt
d4458d1216 Monitor: Store zones as vector instead of list
std::vector suits the usage pattern better with no random inserts/deletions
2021-04-25 22:29:18 +02:00
Peter Keresztes Schmidt
ad32a94931 ZoneStats: Rename members according to Google style guide 2021-04-25 17:18:07 +02:00
Isaac Connor
8671e65517 More work on flushing out ZoneStats. Use references to avoid copying 2021-04-21 21:40:39 -04:00
Isaac Connor
be9c5d3f95 Introduce a ZoneStats structure/class and implement a list of them in packet, frame. Store the stats in a list in the packet until it is time to write them to the db in the event. Hence implement batched queuing of stats. 2021-04-21 17:51:43 -04:00
Isaac Connor
c7f06dda0b Improve copy constructor to use initializers 2021-04-20 17:18:57 -04:00
Isaac Connor
e0fc265263 spacing 2021-04-19 13:19:23 -04:00
Isaac Connor
03767236d9 Clean up constructor/Setup functions. Add copy constructor. Change Load to return a std::list<Zone> 2021-04-17 12:50:26 -04:00
Peter Keresztes Schmidt
9e77324de4 Replace raw mysql_query calls with the zmDb* functions
With this we can make sure we have proper locking of our DB connection at all times.
2021-03-06 00:12:18 +01:00
Isaac Connor
8c2e6589ac fifo.h got split into zm_fifo and zm_fifo_debug so update the code in zm_zone 2021-03-01 16:49:27 -05:00
Peter Keresztes Schmidt
76e6c468e8 rgb: Convert some constant defines to constexpr
Using defines interferes with fmt.
Also rename them according to the Google styleguide.
2021-03-01 00:38:21 +01:00
Isaac Connor
8aeb4ab758 Switch db_mutex to a std::mutex. Use modern locking with it. Use zmDbDo or dbQueue.push where appropriate. code cleanup. 2021-02-25 12:26:26 -05:00
Isaac Connor
124d9bf798 Fix logging when holding db lock 2021-02-19 12:09:02 -05:00