mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-02 15:35:09 -04:00
fix: carry the zone's alarm colour on the alarmed and filtered pixel methods
The alarmed/filtered pixel check methods handed Overlay() the raw GRAY8 scoring mask. Overlay keys on a non-zero source pixel and copies that byte, so the highlight took whatever shape one byte has in the destination format: the red channel on an RGB32 monitor, which is the whole reason alarms have always come out red; all three channels on RGB24, giving white; luma alone on YUV420. The zone's configured Alarm Colour was honoured only on the blob path, which goes through HighlightEdges. Generalise HighlightEdges into BuildHighlight, which takes an edges_only flag and otherwise fills every marked pixel, and build the highlight for the pixel methods the same way the blob path already builds its outline: in the capture's own pixel format, carrying alarm_rgb, once scoring is finished with the GRAY8 mask. HighlightEdges stays as a thin wrapper so the blob path and its callers are unchanged. This is a behaviour change: monitors left on the default red see no difference, but a zone configured with any other Alarm Colour now paints that colour instead of red or white. Reverting the zone hunk fails the new test in all three of its sections. Full suite: 148 cases, 12535 assertions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WBHBB95RBX7D9p8ge2WDZb
This commit is contained in:
1 parent
5b2dd2abbe
commit
bdf688eecb
6 files changed
+279
-27
No files matched your search
+22
-17
@@ -1175,14 +1175,15 @@ void Image::Assign(const Image &image) {
|
||||
}
|
||||
}
|
||||
|
||||
Image *Image::HighlightEdges(
|
||||
Image *Image::BuildHighlight(
|
||||
Rgb colour,
|
||||
unsigned int p_colours,
|
||||
unsigned int p_subpixelorder,
|
||||
const Box *limits
|
||||
const Box *limits,
|
||||
bool edges_only
|
||||
) {
|
||||
if ( imagePixFormat != AV_PIX_FMT_GRAY8 ) {
|
||||
Panic("Attempt to highlight image edges when colours = %d", colours);
|
||||
Panic("Attempt to highlight image when colours = %d", colours);
|
||||
}
|
||||
|
||||
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
|
||||
@@ -1215,12 +1216,13 @@ Image *Image::HighlightEdges(
|
||||
const uint8_t* p = buffer + (y * src_linesize) + lo_x;
|
||||
uint8_t* phigh = high_buff + (y * dst_linesize) + lo_x;
|
||||
for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) {
|
||||
bool edge = false;
|
||||
bool mark = false;
|
||||
if ( *p ) {
|
||||
edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
mark = !edges_only
|
||||
|| (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
|| (y > 0 && !*(p-src_linesize)) || (y < (height-1) && !*(p+src_linesize));
|
||||
}
|
||||
if ( edge ) {
|
||||
if ( mark ) {
|
||||
*phigh = colour;
|
||||
}
|
||||
}
|
||||
@@ -1230,12 +1232,13 @@ Image *Image::HighlightEdges(
|
||||
const uint8_t* p = buffer + (y * src_linesize) + lo_x;
|
||||
uint8_t* phigh = high_buff + (y * dst_linesize) + lo_x * 3;
|
||||
for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh += 3 ) {
|
||||
bool edge = false;
|
||||
bool mark = false;
|
||||
if ( *p ) {
|
||||
edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
mark = !edges_only
|
||||
|| (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
|| (y > 0 && !*(p-src_linesize)) || (y < (height-1) && !*(p+src_linesize));
|
||||
}
|
||||
if ( edge ) {
|
||||
if ( mark ) {
|
||||
RED_PTR_RGBA(phigh) = RED_VAL_RGBA(colour);
|
||||
GREEN_PTR_RGBA(phigh) = GREEN_VAL_RGBA(colour);
|
||||
BLUE_PTR_RGBA(phigh) = BLUE_VAL_RGBA(colour);
|
||||
@@ -1247,12 +1250,13 @@ Image *Image::HighlightEdges(
|
||||
const uint8_t* p = buffer + (y * src_linesize) + lo_x;
|
||||
Rgb* phigh = (Rgb*)(high_buff + (y * dst_linesize) + (lo_x << 2));
|
||||
for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) {
|
||||
bool edge = false;
|
||||
bool mark = false;
|
||||
if ( *p ) {
|
||||
edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
mark = !edges_only
|
||||
|| (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
|| (y > 0 && !*(p-src_linesize)) || (y < (height-1) && !*(p+src_linesize));
|
||||
}
|
||||
if ( edge ) {
|
||||
if ( mark ) {
|
||||
*phigh = colour;
|
||||
}
|
||||
}
|
||||
@@ -1260,24 +1264,25 @@ Image *Image::HighlightEdges(
|
||||
} else if ( zm_is_yuv420(p_pixfmt) ) {
|
||||
// Single alarm colour over a transparent (Y=0) background; Overlay() onto
|
||||
// a YUV420 image keys on a non-zero luma marker. Write the colour's luma
|
||||
// at each edge pixel and its chroma at the shared 2x2 chroma sample.
|
||||
// at each marked pixel and its chroma at the shared 2x2 chroma sample.
|
||||
const YUV yuv = brg_to_yuv(colour);
|
||||
const uint8_t Yc = Y_VAL(yuv), Uc = U_VAL(yuv), Vc = V_VAL(yuv);
|
||||
uint8_t *hplane[4] = {};
|
||||
int hstride[4] = {};
|
||||
if (av_image_fill_arrays(hplane, hstride, high_buff, p_pixfmt, width, height, 32) < 0) {
|
||||
Error("HighlightEdges: av_image_fill_arrays failed for YUV420 %ux%u", width, height);
|
||||
Error("Highlight: av_image_fill_arrays failed for YUV420 %ux%u", width, height);
|
||||
return high_image;
|
||||
}
|
||||
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
|
||||
const uint8_t* p = buffer + (y * src_linesize) + lo_x;
|
||||
for ( unsigned int x = lo_x; x <= hi_x; x++, p++ ) {
|
||||
bool edge = false;
|
||||
bool mark = false;
|
||||
if ( *p ) {
|
||||
edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
mark = !edges_only
|
||||
|| (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1))
|
||||
|| (y > 0 && !*(p-src_linesize)) || (y < (height-1) && !*(p+src_linesize));
|
||||
}
|
||||
if ( edge ) {
|
||||
if ( mark ) {
|
||||
hplane[0][y * hstride[0] + x] = Yc ? Yc : 1; // keep the luma marker non-zero
|
||||
hplane[1][(y / 2) * hstride[1] + (x / 2)] = Uc;
|
||||
hplane[2][(y / 2) * hstride[2] + (x / 2)] = Vc;
|
||||
|
||||
+7
-1
@@ -277,7 +277,13 @@ class Image {
|
||||
uint8 size = 1,
|
||||
Rgb fg_colour = kRGBWhite,
|
||||
Rgb bg_colour = kRGBBlack);
|
||||
Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 );
|
||||
// Build a colour highlight image from this GRAY8 mask. edges_only traces the
|
||||
// outline of each marked region (what the blob check method shows); false
|
||||
// fills every marked pixel (what the alarmed/filtered pixel methods show).
|
||||
Image *BuildHighlight( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0, bool edges_only=true );
|
||||
Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 ) {
|
||||
return BuildHighlight(colour, p_colours, p_subpixelorder, limits, true);
|
||||
}
|
||||
//Image *HighlightEdges( Rgb colour, const Polygon &polygon );
|
||||
void Timestamp(const char *label, SystemTimePoint when, const Vector2 &coord, int label_size);
|
||||
void Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder);
|
||||
|
||||
+31
-9
@@ -154,6 +154,24 @@ void Zone::SetScore(unsigned int nScore) {
|
||||
stats.score_ = nScore;
|
||||
} // end void Zone::SetScore(unsigned int nScore)
|
||||
|
||||
// Replace the GRAY8 analysis mask with a highlight in the capture's own pixel
|
||||
// format, carrying this zone's alarm colour, and take ownership of it in
|
||||
// `image`. Overlay() onto the analysis image is then a same-format copy.
|
||||
// monitor->Colours()==1 aliases both GRAY8 and planar YUV420P, so resolve the
|
||||
// real format first: a true GRAY8 monitor has no colour to carry, so upgrade
|
||||
// its highlight to RGB24; YUV420P keeps its format and carries the alarm
|
||||
// colour in chroma.
|
||||
void Zone::BuildAlarmHighlight(Image *mask, bool edges_only) {
|
||||
AVPixelFormat capture_fmt = zm_pixformat_from_colours(monitor->Colours(), monitor->SubpixelOrder());
|
||||
if (capture_fmt == AV_PIX_FMT_GRAY8) {
|
||||
image = mask->BuildHighlight(alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent(), edges_only);
|
||||
} else {
|
||||
image = mask->BuildHighlight(alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent(), edges_only);
|
||||
}
|
||||
// 'image' is now detached from the mask we were handed, so that is ours to free.
|
||||
delete mask;
|
||||
}
|
||||
|
||||
void Zone::SetAlarmImage(const Image* srcImage) {
|
||||
if ( image )
|
||||
delete image;
|
||||
@@ -795,15 +813,19 @@ bool Zone::CheckAlarms(const Image *delta_image) {
|
||||
// aliases both GRAY8 and planar YUV420P, so resolve the real format first:
|
||||
// a true GRAY8 monitor has no colour to carry, so upgrade its highlight to
|
||||
// RGB24; YUV420P keeps its format and carries the alarm colour in chroma.
|
||||
AVPixelFormat capture_fmt = zm_pixformat_from_colours(monitor->Colours(), monitor->SubpixelOrder());
|
||||
if (capture_fmt == AV_PIX_FMT_GRAY8) {
|
||||
image = diff_image->HighlightEdges(alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent());
|
||||
} else {
|
||||
image = diff_image->HighlightEdges(alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent());
|
||||
}
|
||||
|
||||
// Only need to delete this when 'image' becomes detached and points somewhere else
|
||||
delete diff_image;
|
||||
BuildAlarmHighlight(diff_image, true);
|
||||
diff_image = nullptr;
|
||||
} else if ((type < PRECLUSIVE) && (monitor->GetOptSaveJPEGs() > 1)) {
|
||||
// Alarmed/filtered pixels: the mask stays GRAY8 through the analysis
|
||||
// above because the scoring reads it as one byte per pixel. Overlay()
|
||||
// onto the analysis image then keys on a non-zero source pixel, so a
|
||||
// GRAY8 mask painted an undifferentiated highlight -- writing the mask
|
||||
// value into the red channel on an RGB32 monitor (hence "alarms are
|
||||
// always red"), into all three on RGB24 (white), and into luma alone on
|
||||
// YUV420 -- and the zone's configured Alarm Colour was honoured only on
|
||||
// the blob path. Build the same colour highlight the blob path builds,
|
||||
// filled rather than outlined, now that scoring is done with the mask.
|
||||
BuildAlarmHighlight(diff_image, false);
|
||||
diff_image = nullptr;
|
||||
} // end if ( (type < PRECLUSIVE) && (check_method >= BLOBS) && (monitor->GetOptSaveJPEGs() > 1)
|
||||
|
||||
|
||||
@@ -226,6 +226,7 @@ class Zone {
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
void BuildAlarmHighlight(Image *mask, bool edges_only);
|
||||
|
||||
inline const Image *getPgImage() const { return pg_image; }
|
||||
inline const Range *getRanges() const { return ranges; }
|
||||
|
||||
Reference in new issue
Block a user