mirror of
https://github.com/Motion-Project/motion.git
synced 2025-12-23 23:18:21 -05:00
698 lines
24 KiB
C++
698 lines
24 KiB
C++
/*
|
|
* This file is part of Motionplus.
|
|
*
|
|
* MotionPlus is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Motionplus is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Motionplus. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* Copyright 2020 MotionMrDave@gmail.com
|
|
*/
|
|
#include "motionplus.hpp"
|
|
#include "conf.hpp"
|
|
#include "logger.hpp"
|
|
#include "util.hpp"
|
|
#include "picture.hpp"
|
|
#include "jpegutils.hpp"
|
|
#include "event.hpp"
|
|
#include "exif.hpp"
|
|
#include "draw.hpp"
|
|
|
|
#ifdef HAVE_WEBP
|
|
#include <webp/encode.h>
|
|
#include <webp/mux.h>
|
|
#endif /* HAVE_WEBP */
|
|
|
|
|
|
|
|
#ifdef HAVE_WEBP
|
|
/*
|
|
* pic_webp_exif writes the EXIF APP1 chunk to the webp file.
|
|
* It must be called after WebPEncode() and the result
|
|
* can then be written out to webp a file
|
|
*/
|
|
static void pic_webp_exif(WebPMux* webp_mux,
|
|
const struct ctx_cam *cam,
|
|
const struct timespec *ts1,
|
|
const struct ctx_coord *box)
|
|
{
|
|
unsigned char *exif = NULL;
|
|
unsigned exif_len = exif_prepare(&exif, cam, ts1, box);
|
|
|
|
if(exif_len > 0) {
|
|
WebPData webp_exif;
|
|
/* EXIF in WEBP does not need the EXIF marker signature (6 bytes) that are needed by jpeg */
|
|
webp_exif.bytes = exif + 6;
|
|
webp_exif.size = exif_len - 6;
|
|
|
|
WebPMuxError err = WebPMuxSetChunk(webp_mux, "EXIF", &webp_exif, 1);
|
|
if (err != WEBP_MUX_OK) {
|
|
MOTION_LOG(ERR, TYPE_CORE, NO_ERRNO
|
|
, _("Unable to set set EXIF to webp chunk"));
|
|
}
|
|
free(exif);
|
|
}
|
|
}
|
|
#endif /* HAVE_WEBP */
|
|
|
|
|
|
|
|
/** Save image as webp to file */
|
|
static void pic_save_webp(FILE *fp, unsigned char *image, int width, int height,
|
|
int quality, struct ctx_cam *cam, struct timespec *ts1, struct ctx_coord *box)
|
|
{
|
|
#ifdef HAVE_WEBP
|
|
/* Create a config present and check for compatible library version */
|
|
WebPConfig webp_config;
|
|
if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)){
|
|
MOTION_LOG(ERR, TYPE_CORE, NO_ERRNO, _("libwebp version error"));
|
|
return;
|
|
}
|
|
|
|
/* Create the input data structure and check for compatible library version */
|
|
WebPPicture webp_image;
|
|
if (!WebPPictureInit(&webp_image)){
|
|
MOTION_LOG(ERR, TYPE_CORE, NO_ERRNO,_("libwebp version error"));
|
|
return;
|
|
}
|
|
|
|
/* Allocate the image buffer based on image width and height */
|
|
webp_image.width = width;
|
|
webp_image.height = height;
|
|
if (!WebPPictureAlloc(&webp_image)){
|
|
MOTION_LOG(ERR, TYPE_CORE, NO_ERRNO,_("libwebp image buffer allocation error"));
|
|
return;
|
|
}
|
|
|
|
/* Map the input YUV420P buffer as individual Y, U and V pointers */
|
|
webp_image.y = image;
|
|
webp_image.u = image + width * height;
|
|
webp_image.v = webp_image.u + (width * height) / 4;
|
|
|
|
/* Setup the memory writting method */
|
|
WebPMemoryWriter webp_writer;
|
|
WebPMemoryWriterInit(&webp_writer);
|
|
webp_image.writer = WebPMemoryWrite;
|
|
webp_image.custom_ptr = (void*) &webp_writer;
|
|
|
|
/* Encode the YUV image as webp */
|
|
if (!WebPEncode(&webp_config, &webp_image))
|
|
MOTION_LOG(WRN, TYPE_CORE, NO_ERRNO,_("libwebp image compression error"));
|
|
|
|
/* A bitstream object is needed for the muxing proces */
|
|
WebPData webp_bitstream;
|
|
webp_bitstream.bytes = webp_writer.mem;
|
|
webp_bitstream.size = webp_writer.size;
|
|
|
|
/* Create a mux from the prepared image data */
|
|
WebPMux* webp_mux = WebPMuxCreate(&webp_bitstream, 1);
|
|
pic_webp_exif(webp_mux, cam, ts1, box);
|
|
|
|
/* Add Exif data to the webp image data */
|
|
WebPData webp_output;
|
|
WebPMuxError err = WebPMuxAssemble(webp_mux, &webp_output);
|
|
if (err != WEBP_MUX_OK) {
|
|
MOTION_LOG(ERR, TYPE_CORE, NO_ERRNO,_("unable to assemble webp image"));
|
|
}
|
|
|
|
/* Write the webp final bitstream to the file */
|
|
if (fwrite(webp_output.bytes, sizeof(uint8_t), webp_output.size, fp) != webp_output.size)
|
|
MOTION_LOG(ERR, TYPE_CORE, NO_ERRNO,_("unable to save webp image to file"));
|
|
|
|
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
/* writer.mem must be freed by calling WebPMemoryWriterClear */
|
|
WebPMemoryWriterClear(&webp_writer);
|
|
#else
|
|
/* writer.mem must be freed by calling 'free(writer.mem)' */
|
|
free(webp_writer.mem);
|
|
#endif /* WEBP_ENCODER_ABI_VERSION */
|
|
|
|
/* free the memory used by webp for image data */
|
|
WebPPictureFree(&webp_image);
|
|
/* free the memory used by webp mux object */
|
|
WebPMuxDelete(webp_mux);
|
|
/* free the memory used by webp for output data */
|
|
WebPDataClear(&webp_output);
|
|
#else
|
|
(void)fp;
|
|
(void)image;
|
|
(void)width;
|
|
(void)height;
|
|
(void) quality;
|
|
(void)cam;
|
|
(void)ts1;
|
|
(void)box;
|
|
#endif /* HAVE_WEBP */
|
|
}
|
|
|
|
|
|
/** Save image as yuv420p jpeg to file */
|
|
static void pic_save_yuv420p(FILE *fp, unsigned char *image, int width, int height,
|
|
int quality, struct ctx_cam *cam, struct timespec *ts1, struct ctx_coord *box) {
|
|
|
|
int sz = 0;
|
|
int image_size = cam->imgs.size_norm;
|
|
unsigned char *buf =(unsigned char*) mymalloc(image_size);
|
|
|
|
sz = jpgutl_put_yuv420p(buf, image_size, image, width, height, quality, cam ,ts1, box);
|
|
fwrite(buf, sz, 1, fp);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
/** Save image as grey jpeg to file */
|
|
static void pic_save_grey(FILE *picture, unsigned char *image, int width, int height,
|
|
int quality, struct ctx_cam *cam, struct timespec *ts1, struct ctx_coord *box) {
|
|
|
|
int sz = 0;
|
|
int image_size = cam->imgs.size_norm;
|
|
unsigned char *buf =(unsigned char*) mymalloc(image_size);
|
|
|
|
sz = jpgutl_put_grey(buf, image_size, image, width, height, quality, cam ,ts1, box);
|
|
fwrite(buf, sz, 1, picture);
|
|
|
|
free(buf);
|
|
}
|
|
|
|
/** Save image as greyscale ppm image to file */
|
|
static void pic_save_ppm(FILE *picture, unsigned char *image, int width, int height) {
|
|
int x, y;
|
|
unsigned char *l = image;
|
|
unsigned char *u = image + width * height;
|
|
unsigned char *v = u + (width * height) / 4;
|
|
int r, g, b;
|
|
unsigned char rgb[3];
|
|
|
|
/*
|
|
* ppm header
|
|
* width height
|
|
* maxval
|
|
*/
|
|
fprintf(picture, "P6\n");
|
|
fprintf(picture, "%d %d\n", width, height);
|
|
fprintf(picture, "%d\n", 255);
|
|
for (y = 0; y < height; y++) {
|
|
|
|
for (x = 0; x < width; x++) {
|
|
r = 76283 * (((int)*l) - 16)+104595*(((int)*u) - 128);
|
|
g = 76283 * (((int)*l) - 16)- 53281*(((int)*u) - 128) - 25625 * (((int)*v) - 128);
|
|
b = 76283 * (((int)*l) - 16) + 132252 * (((int)*v) - 128);
|
|
r = r >> 16;
|
|
g = g >> 16;
|
|
b = b >> 16;
|
|
if (r < 0)
|
|
r = 0;
|
|
else if (r > 255)
|
|
r = 255;
|
|
if (g < 0)
|
|
g = 0;
|
|
else if (g > 255)
|
|
g = 255;
|
|
if (b < 0)
|
|
b = 0;
|
|
else if (b > 255)
|
|
b = 255;
|
|
|
|
rgb[0] = b;
|
|
rgb[1] = g;
|
|
rgb[2] = r;
|
|
|
|
l++;
|
|
if (x%2 != 0) {
|
|
u++;
|
|
v++;
|
|
}
|
|
/* ppm is rgb not bgr */
|
|
fwrite(rgb, 1, 3, picture);
|
|
}
|
|
if (y%2 == 0) {
|
|
u -= width / 2;
|
|
v -= width / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Put picture into memory as jpg */
|
|
int pic_put_memory(struct ctx_cam *cam, unsigned char* dest_image, int image_size, unsigned char *image,
|
|
int quality, int width, int height) {
|
|
|
|
struct timespec ts1;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts1);
|
|
if (!cam->conf->stream_grey){
|
|
return jpgutl_put_yuv420p(dest_image, image_size, image,
|
|
width, height, quality, cam ,&ts1, NULL);
|
|
} else {
|
|
return jpgutl_put_grey(dest_image, image_size, image,
|
|
width, height, quality, cam,&ts1, NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Write the picture to a file */
|
|
static void pic_write(struct ctx_cam *cam, FILE *picture, unsigned char *image, int quality, int ftype){
|
|
|
|
int width, height;
|
|
int passthrough;
|
|
|
|
passthrough = mycheck_passthrough(cam);
|
|
if ((ftype == FTYPE_IMAGE) && (cam->imgs.size_high > 0) && (!passthrough)) {
|
|
width = cam->imgs.width_high;
|
|
height = cam->imgs.height_high;
|
|
} else {
|
|
width = cam->imgs.width;
|
|
height = cam->imgs.height;
|
|
}
|
|
|
|
if (cam->conf->picture_type == "ppm") {
|
|
pic_save_ppm(picture, image, width, height);
|
|
} else if (cam->conf->picture_type == "webp") {
|
|
pic_save_webp(picture, image, width, height, quality, cam
|
|
, &(cam->current_image->imgts), &(cam->current_image->location));
|
|
} else if (cam->conf->picture_type == "grey") {
|
|
pic_save_grey(picture, image, width, height, quality, cam
|
|
, &(cam->current_image->imgts), &(cam->current_image->location));
|
|
} else {
|
|
pic_save_yuv420p(picture, image, width, height, quality, cam
|
|
, &(cam->current_image->imgts), &(cam->current_image->location));
|
|
}
|
|
}
|
|
|
|
/* Saves image to a file in format requested */
|
|
void pic_save_norm(struct ctx_cam *cam, char *file, unsigned char *image, int ftype) {
|
|
FILE *picture;
|
|
|
|
picture = myfopen(file, "w");
|
|
if (!picture) {
|
|
/* Report to syslog - suggest solution if the problem is access rights to target dir. */
|
|
if (errno == EACCES) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Can't write picture to file %s - check access rights to target directory\n"
|
|
"Thread is going to finish due to this fatal error"), file);
|
|
cam->finish_cam = 1;
|
|
cam->restart_cam = 0;
|
|
return;
|
|
} else {
|
|
/* If target dir is temporarily unavailable we may survive. */
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Can't write picture to file %s"), file);
|
|
return;
|
|
}
|
|
}
|
|
|
|
pic_write(cam, picture, image, cam->conf->picture_quality, ftype);
|
|
|
|
myfclose(picture);
|
|
}
|
|
|
|
/* Saves image to a file in format requested */
|
|
void pic_save_roi(struct ctx_cam *cam, char *file, unsigned char *image) {
|
|
FILE *picture;
|
|
int image_size, sz, indxh;
|
|
ctx_coord *bx;
|
|
unsigned char *buf, *img;
|
|
int width,height, x, y;
|
|
|
|
bx = &cam->current_image->location;
|
|
|
|
if ((bx->width <64) || (bx->height <64)) return;
|
|
|
|
picture = myfopen(file, "w");
|
|
if (!picture) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Can't write picture to file %s"), file);
|
|
return;
|
|
}
|
|
|
|
/* Lets make the box square */
|
|
|
|
width = bx->width;
|
|
height= bx->height;
|
|
|
|
if (width > cam->imgs.height) width =cam->imgs.height;
|
|
if (height > cam->imgs.width) height =cam->imgs.width;
|
|
|
|
if (width > height){
|
|
height= width;
|
|
x = bx->minx;
|
|
y = bx->miny - ((width - height)/2);
|
|
if (y < 0) y = 0;
|
|
if ((y+height) > cam->imgs.height) y = cam->imgs.height - height;
|
|
} else {
|
|
width = height;
|
|
x = bx->minx - ((height - width)/2);
|
|
y = bx->miny;
|
|
if (x < 0) x = 0;
|
|
if ((x+width) > cam->imgs.width) x = cam->imgs.width - width;
|
|
}
|
|
|
|
image_size = width * height;
|
|
|
|
buf =(unsigned char*) mymalloc(image_size);
|
|
img =(unsigned char*) mymalloc(image_size);
|
|
|
|
for (indxh=y; indxh<y+height; indxh++){
|
|
memcpy(img+((indxh-y)*width), image+(indxh*cam->imgs.width)+x, width);
|
|
}
|
|
|
|
sz = jpgutl_put_grey(buf, image_size, img
|
|
,width, height
|
|
,cam->conf->picture_quality, cam
|
|
,&(cam->current_image->imgts), bx);
|
|
|
|
fwrite(buf, sz, 1, picture);
|
|
|
|
free(buf);
|
|
free(img);
|
|
|
|
myfclose(picture);
|
|
}
|
|
|
|
/** Get the pgm file used as fixed mask */
|
|
unsigned char *pic_load_pgm(FILE *picture, int width, int height) {
|
|
|
|
int x, y, mask_width, mask_height, maxval;
|
|
char line[256];
|
|
unsigned char *image, *resized_image;
|
|
|
|
line[255] = 0;
|
|
|
|
if (!fgets(line, 255, picture)) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO,_("Could not read from pgm file"));
|
|
return NULL;
|
|
}
|
|
|
|
if (strncmp(line, "P5", 2)) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("This is not a pgm file, starts with '%s'"), line);
|
|
return NULL;
|
|
}
|
|
|
|
/* Skip comment */
|
|
line[0] = '#';
|
|
while (line[0] == '#')
|
|
if (!fgets(line, 255, picture))
|
|
return NULL;
|
|
|
|
/* Read image size */
|
|
if (sscanf(line, "%d %d", &mask_width, &mask_height) != 2) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Failed reading size in pgm file"));
|
|
return NULL;
|
|
}
|
|
|
|
/* Maximum value */
|
|
line[0] = '#';
|
|
while (line[0] == '#')
|
|
if (!fgets(line, 255, picture))
|
|
return NULL;
|
|
|
|
if (sscanf(line, "%d", &maxval) != 1) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Failed reading maximum value in pgm file"));
|
|
return NULL;
|
|
}
|
|
|
|
/* Read data */
|
|
/* We allocate the size for a 420P since we will use
|
|
** this image for masking privacy which needs the space for
|
|
** the cr / cb components
|
|
*/
|
|
image =(unsigned char*) mymalloc((mask_width * mask_height * 3) / 2);
|
|
|
|
for (y = 0; y < mask_height; y++) {
|
|
if ((int)fread(&image[y * mask_width], 1, mask_width, picture) != mask_width)
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "Failed reading image data from pgm file");
|
|
|
|
for (x = 0; x < mask_width; x++)
|
|
image[y * mask_width + x] = (int)image[y * mask_width + x] * 255 / maxval;
|
|
|
|
}
|
|
|
|
/* Resize mask if required */
|
|
if (mask_width != width || mask_height != height) {
|
|
MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
|
|
,_("The mask file specified is not the same size as image from camera."));
|
|
MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
|
|
,_("Attempting to resize mask image from %dx%d to %dx%d")
|
|
,mask_width, mask_height, width, height);
|
|
|
|
resized_image =(unsigned char*) mymalloc((width * height * 3) / 2);
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
resized_image[y * width + x] = image[
|
|
(mask_height - 1) * y / (height - 1) * mask_width +
|
|
(mask_width - 1) * x / (width - 1)];
|
|
}
|
|
}
|
|
|
|
free(image);
|
|
image = resized_image;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
/** Write out a base mask file if needed */
|
|
static void pic_write_mask(struct ctx_cam *cam, const char *file) {
|
|
FILE *picture;
|
|
|
|
picture = myfopen(file, "w");
|
|
if (!picture) {
|
|
/* Report to syslog - suggest solution if the problem is access rights to target dir. */
|
|
if (errno == EACCES) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("can't write mask file %s - check access rights to target directory")
|
|
,file);
|
|
} else {
|
|
/* If target dir is temporarily unavailable we may survive. */
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("can't write mask file %s"), file);
|
|
}
|
|
return;
|
|
}
|
|
memset(cam->imgs.image_motion.image_norm, 255, cam->imgs.motionsize); /* Initialize to unset */
|
|
|
|
/* Write pgm-header. */
|
|
fprintf(picture, "P5\n");
|
|
fprintf(picture, "%d %d\n", cam->conf->width, cam->conf->height);
|
|
fprintf(picture, "%d\n", 255);
|
|
|
|
/* Write pgm image data at once. */
|
|
if ((int)fwrite(cam->imgs.image_motion.image_norm, cam->conf->width, cam->conf->height, picture) != cam->conf->height) {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Failed writing default mask as pgm file"));
|
|
return;
|
|
}
|
|
|
|
myfclose(picture);
|
|
|
|
MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
|
|
,_("Creating empty mask %s\nPlease edit this file and "
|
|
"re-run motion to enable mask feature"), cam->conf->mask_file.c_str());
|
|
}
|
|
|
|
void pic_scale_img(int width_src, int height_src, unsigned char *img_src, unsigned char *img_dst){
|
|
|
|
int i = 0, x, y;
|
|
for (y = 0; y < height_src; y+=2)
|
|
for (x = 0; x < width_src; x+=2)
|
|
img_dst[i++] = img_src[y * width_src + x];
|
|
|
|
for (y = 0; y < height_src / 2; y+=2)
|
|
for (x = 0; x < width_src; x += 4)
|
|
{
|
|
img_dst[i++] = img_src[(width_src * height_src) + (y * width_src) + x];
|
|
img_dst[i++] = img_src[(width_src * height_src) + (y * width_src) + (x + 1)];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void pic_save_preview(struct ctx_cam *cam, struct ctx_image_data *img) {
|
|
unsigned char *image_norm, *image_high;
|
|
|
|
/* Save our pointers to our memory locations for images*/
|
|
image_norm = cam->imgs.image_preview.image_norm;
|
|
image_high = cam->imgs.image_preview.image_high;
|
|
|
|
/* Copy over the meta data from the img into preview */
|
|
memcpy(&cam->imgs.image_preview, img, sizeof(struct ctx_image_data));
|
|
|
|
/* Restore the pointers to the memory locations for images*/
|
|
cam->imgs.image_preview.image_norm = image_norm;
|
|
cam->imgs.image_preview.image_high = image_high;
|
|
|
|
/* Copy the actual images for norm and high */
|
|
memcpy(cam->imgs.image_preview.image_norm, img->image_norm, cam->imgs.size_norm);
|
|
if (cam->imgs.size_high > 0){
|
|
memcpy(cam->imgs.image_preview.image_high, img->image_high, cam->imgs.size_high);
|
|
}
|
|
|
|
/*
|
|
* If we set output_all to yes and during the event
|
|
* there is no image with motion, diffs is 0, we are not going to save the preview event
|
|
*/
|
|
if (cam->imgs.image_preview.diffs == 0)
|
|
cam->imgs.image_preview.diffs = 1;
|
|
|
|
draw_locate_preview(cam, img);
|
|
|
|
}
|
|
|
|
void pic_init_privacy(struct ctx_cam *cam){
|
|
|
|
int indxrow, indxcol;
|
|
int start_cr, offset_cb, start_cb;
|
|
int y_index, uv_index;
|
|
int indx_img, indx_max; /* Counter and max for norm/high */
|
|
int indx_width, indx_height;
|
|
unsigned char *img_temp, *img_temp_uv;
|
|
|
|
|
|
FILE *picture;
|
|
|
|
/* Load the privacy file if any */
|
|
cam->imgs.mask_privacy = NULL;
|
|
cam->imgs.mask_privacy_uv = NULL;
|
|
cam->imgs.mask_privacy_high = NULL;
|
|
cam->imgs.mask_privacy_high_uv = NULL;
|
|
|
|
if (cam->conf->mask_privacy != "") {
|
|
if ((picture = myfopen(cam->conf->mask_privacy.c_str(), "r"))) {
|
|
MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Opening privacy mask file"));
|
|
/*
|
|
* NOTE: The mask is expected to have the output dimensions. I.e., the mask
|
|
* applies to the already rotated image, not the capture image. Thus, use
|
|
* width and height from imgs.
|
|
*/
|
|
cam->imgs.mask_privacy = pic_load_pgm(picture, cam->imgs.width, cam->imgs.height);
|
|
|
|
/* We only need the "or" mask for the U & V chrominance area. */
|
|
cam->imgs.mask_privacy_uv =(unsigned char*) mymalloc((cam->imgs.height * cam->imgs.width) / 2);
|
|
if (cam->imgs.size_high > 0){
|
|
MOTION_LOG(INF, TYPE_ALL, NO_ERRNO
|
|
,_("Opening high resolution privacy mask file"));
|
|
rewind(picture);
|
|
cam->imgs.mask_privacy_high = pic_load_pgm(picture, cam->imgs.width_high, cam->imgs.height_high);
|
|
cam->imgs.mask_privacy_high_uv =(unsigned char*) mymalloc((cam->imgs.height_high * cam->imgs.width_high) / 2);
|
|
}
|
|
|
|
myfclose(picture);
|
|
} else {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Error opening mask file %s"), cam->conf->mask_privacy.c_str());
|
|
/* Try to write an empty mask file to make it easier for the user to edit it */
|
|
pic_write_mask(cam, cam->conf->mask_privacy.c_str() );
|
|
}
|
|
|
|
if (!cam->imgs.mask_privacy) {
|
|
MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
|
|
,_("Failed to read mask privacy image. Mask privacy feature disabled."));
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_ALL, NO_ERRNO
|
|
,_("Mask privacy file \"%s\" loaded."), cam->conf->mask_privacy.c_str());
|
|
|
|
indx_img = 1;
|
|
indx_max = 1;
|
|
if (cam->imgs.size_high > 0) indx_max = 2;
|
|
|
|
while (indx_img <= indx_max){
|
|
if (indx_img == 1){
|
|
start_cr = (cam->imgs.height * cam->imgs.width);
|
|
offset_cb = ((cam->imgs.height * cam->imgs.width)/4);
|
|
start_cb = start_cr + offset_cb;
|
|
indx_width = cam->imgs.width;
|
|
indx_height = cam->imgs.height;
|
|
img_temp = cam->imgs.mask_privacy;
|
|
img_temp_uv = cam->imgs.mask_privacy_uv;
|
|
} else {
|
|
start_cr = (cam->imgs.height_high * cam->imgs.width_high);
|
|
offset_cb = ((cam->imgs.height_high * cam->imgs.width_high)/4);
|
|
start_cb = start_cr + offset_cb;
|
|
indx_width = cam->imgs.width_high;
|
|
indx_height = cam->imgs.height_high;
|
|
img_temp = cam->imgs.mask_privacy_high;
|
|
img_temp_uv = cam->imgs.mask_privacy_high_uv;
|
|
}
|
|
|
|
for (indxrow = 0; indxrow < indx_height; indxrow++) {
|
|
for (indxcol = 0; indxcol < indx_width; indxcol++) {
|
|
y_index = indxcol + (indxrow * indx_width);
|
|
if (img_temp[y_index] == 0xff) {
|
|
if ((indxcol % 2 == 0) && (indxrow % 2 == 0) ){
|
|
uv_index = (indxcol/2) + ((indxrow * indx_width)/4);
|
|
img_temp[start_cr + uv_index] = 0xff;
|
|
img_temp[start_cb + uv_index] = 0xff;
|
|
img_temp_uv[uv_index] = 0x00;
|
|
img_temp_uv[offset_cb + uv_index] = 0x00;
|
|
}
|
|
} else {
|
|
img_temp[y_index] = 0x00;
|
|
if ((indxcol % 2 == 0) && (indxrow % 2 == 0) ){
|
|
uv_index = (indxcol/2) + ((indxrow * indx_width)/4);
|
|
img_temp[start_cr + uv_index] = 0x00;
|
|
img_temp[start_cb + uv_index] = 0x00;
|
|
img_temp_uv[uv_index] = 0x80;
|
|
img_temp_uv[offset_cb + uv_index] = 0x80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
indx_img++;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void pic_init_mask(struct ctx_cam *cam){
|
|
|
|
FILE *picture;
|
|
|
|
/* Load the mask file if any */
|
|
if (cam->conf->mask_file != "") {
|
|
if ((picture = myfopen(cam->conf->mask_file.c_str(), "r"))) {
|
|
/*
|
|
* NOTE: The mask is expected to have the output dimensions. I.e., the mask
|
|
* applies to the already rotated image, not the capture image. Thus, use
|
|
* width and height from imgs.
|
|
*/
|
|
cam->imgs.mask = pic_load_pgm(picture, cam->imgs.width, cam->imgs.height);
|
|
myfclose(picture);
|
|
} else {
|
|
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO
|
|
,_("Error opening mask file %s")
|
|
,cam->conf->mask_file.c_str());
|
|
/*
|
|
* Try to write an empty mask file to make it easier
|
|
* for the user to edit it
|
|
*/
|
|
pic_write_mask(cam, cam->conf->mask_file.c_str());
|
|
}
|
|
|
|
if (!cam->imgs.mask) {
|
|
MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO
|
|
,_("Failed to read mask image. Mask feature disabled."));
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_ALL, NO_ERRNO
|
|
,_("Maskfile \"%s\" loaded.")
|
|
,cam->conf->mask_file.c_str());
|
|
}
|
|
} else {
|
|
cam->imgs.mask = NULL;
|
|
}
|
|
}
|