Skip to content

Commit

Permalink
Make motion fatigue more configurable and add a skip factor where mot…
Browse files Browse the repository at this point in the history
…ion fatigue is ignored every n'th frame, which

is useful for workshop timelapses (not so much for outdoor use)
  • Loading branch information
Matthias-Wandel committed Jul 2, 2020
1 parent 9b666e4 commit 2931614
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 38 deletions.
45 changes: 26 additions & 19 deletions src/compare.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ int NewestAverageBright;
static ImgMap_t * DiffVal = NULL;
ImgMap_t * WeightMap = NULL;

static TriggerInfo_t SearchDiffMaxWindow(Region_t Region, int threshold);
static TriggerInfo_t AnalyzeDifferences(Region_t Region, int threshold, int UpdateFatigue, int SubtractFatigue);


//----------------------------------------------------------------------------------------
// Compare two images in memory
// Pic1 is previous pic, pic2 is latest pic.
//----------------------------------------------------------------------------------------
TriggerInfo_t ComparePix(MemImage_t * pic1, MemImage_t * pic2, char * DebugImgName)
TriggerInfo_t ComparePix(MemImage_t * pic1, MemImage_t * pic2,
int UpdateFatigue, int SkipFatigue, char * DebugImgName)
{
int width, height, bPerRow;
int row, col;
Expand Down Expand Up @@ -241,13 +242,13 @@ TriggerInfo_t ComparePix(MemImage_t * pic1, MemImage_t * pic2, char * DebugImgNa
}
}

TriggerInfo_t Trigger;
int threshold;
{
// Try to gauge the noise level of the difference maps using the built histogram.
// Gauge the difference noise level of the difference maps using the built histogram.
// assuming two thirds of the image has not changed
int cumsum = 0;
int threshold;
int twothirds = DetectionPixels*2/3;
TriggerInfo_t Trigger;

for (a=0;a<256;a++){
if (cumsum >= twothirds) break;
Expand All @@ -264,19 +265,20 @@ TriggerInfo_t ComparePix(MemImage_t * pic1, MemImage_t * pic2, char * DebugImgNa
}else{
if (Verbosity) printf("2/3 of image is below %d diff. Using %d threshold\n",a, threshold);
}

// Search for a window with the largest difference in it
Trigger = SearchDiffMaxWindow(MainReg, threshold);
return Trigger;
}

// Apply motion fatigure and search for a window with the largest difference in it
Trigger = AnalyzeDifferences(MainReg, threshold, UpdateFatigue, SkipFatigue);
return Trigger;
}

//----------------------------------------------------------------------------------------
// Search for an N x N window with the maximum differences in it.
// This for rejecting spurious differences outdoors where grass and leaves can cause a
// lot of weak spurious motion over large areas.
// Compute and apply motion fatigue then Search for an N x N window
// with the maximum differences in it.
// This for rejecting spurious differences outdoors where we dont want grass and leaves
// moving in the wind (covering large parts of the image) to trigger motion events.
//----------------------------------------------------------------------------------------
static TriggerInfo_t SearchDiffMaxWindow(Region_t Region, int threshold)
static TriggerInfo_t AnalyzeDifferences(Region_t Region, int threshold, int UpdateFatigue, int SkipFatigue)
{
TriggerInfo_t retval;

Expand Down Expand Up @@ -340,7 +342,7 @@ static TriggerInfo_t SearchDiffMaxWindow(Region_t Region, int threshold)
ShowImgMap(DiffScaled, 100);
}

if (MotionFatigueTc > 0){
if (MotionFatigueTc > 0 && UpdateFatigue){
static int SinceFatiguePrint = 0;
int FatigueAverage;
// Compute motion fatigue
Expand All @@ -365,16 +367,21 @@ static TriggerInfo_t SearchDiffMaxWindow(Region_t Region, int threshold)
SinceFatiguePrint = 0;
}
SinceFatiguePrint++;

}

if (MotionFatigueTc > 0 && SkipFatigue == 0){
// Subtract out motion fatigue

BloomImgMap(Fatigue, FatigueBl); // Use max of cell and eight neighbours for motion fatigue.
//BloomImgMap(FatigueBl, FatigueBl2); // Use max of cell and eight neighbours for motion fatigue.

// Possibly bloom the fatigue map a bit more (may or may not want that)
//BloomImgMap(FatigueBl, FatigueBl2); // Use max of cell and eight neighbours for motion fatigue.

int fatmult = FatigueGainPercent * 3 * 256 / 100;
for (int row=0;row<heightSc;row++){
for (int col=0;col<widthSc;col++){
int ds, FatM;
FatM = FatigueBl->values[row*widthSc+col];
ds = DiffScaled->values[row*widthSc+col] - FatM*3;
int FatSub = (FatigueBl->values[row*widthSc+col] * fatmult) >> 8;
int ds = DiffScaled->values[row*widthSc+col] - FatSub;
if (ds < 0) ds = 0;
DiffScaled->values[row*widthSc+col] = ds;
}
Expand Down
2 changes: 1 addition & 1 deletion src/compare_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ void ShowImgMap(ImgMap_t * map, int divisor)
for (int c=0;c<map->w;c++) fprintf(Log,"%2d",c%10);
fprintf(Log,"\n");
for (int r=0;r<map->h;r++){
fprintf(Log,"%d",r%10 == 0 ? '=' : '|');
fprintf(Log,"%c",r%10 == 0 ? '=' : '|');
for (int c=0;c<map->w;c++){
const char LowDigits[20] = " - = 3 4~5~6~7=8=9";
int v = map->values[r*w+c]/divisor;
Expand Down
15 changes: 12 additions & 3 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,23 @@ static int parse_parameter (const char * tag, const char * value)
// inside a config file.
}else if (keymatch(tag, "postmotion", 10)) {
if (sscanf(value, "%d", &PostMotionKeep) != 1) return -1;
}else if (keymatch(tag, "premotion", 9)) {
if (sscanf(value, "%d", &PreMotionKeep) != 1) return -1;
if (PreMotionKeep != 0 && PreMotionKeep != 1){
fprintf(stderr, "PreMotionKeep may only be 0 or 1\n");
}
}else if (keymatch(tag, "brmonitor", 5)) {
if (sscanf(value, "%d", &BrightnessChangeRestart) != 1) return -1;
}else if (keymatch(tag, "relaunch_timeout", 16)) {
if (sscanf(value, "%d", &relaunch_timeout) != 1) return -1;
}else if (keymatch(tag, "give_up_timeout", 15)) {
if (sscanf(value, "%d", &give_up_timeout) != 1) return -1;
}else if (keymatch(tag, "fatigue", 7)) {
if (sscanf(value, "%d", &MotionFatigueTc) != 1) return -1;
if (sscanf(value, "%d", &give_up_timeout) != 1) return -1;
}else if (strcmp(tag, "fatigue") == 0 || keymatch(tag, "fatigue_tc", 10)) {
if (sscanf(value, "%d", &MotionFatigueTc) != 1) return -1;
}else if (keymatch(tag, "fatigue_gain_percent", 20)) {
if (sscanf(value, "%d", &FatigueGainPercent) != 1) return -1;
}else if (keymatch(tag, "fatigue_skip_count", 18)) {
if (sscanf(value, "%d", &FatigueSkipCount) != 1) return -1;
} else if (keymatch(tag, "scale", 5)) {
// Scale the output image by a fraction 1/N.
if (sscanf(value, "%d", &ScaleDenom) != 1) return -1;
Expand Down
3 changes: 3 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ extern char SaveNames[200];
extern int FollowDir;
extern int ScaleDenom;
extern int SpuriousReject;
extern int PreMotionKeep;
extern int PostMotionKeep;

extern int BrightnessChangeRestart;
extern int MotionFatigueTc;
extern int FatigueGainPercent;
extern int FatigueSkipCount;

extern char DiffMapFileName[200];
extern Regions_t Regions;
Expand Down
5 changes: 3 additions & 2 deletions src/imgcomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ void BloomImgMap(ImgMap_t * src, ImgMap_t * dst);
int BlockFilterImgMap(ImgMap_t * src, ImgMap_t * dst, int fw, int fh, int * pmaxc, int * pmaxr);


// compare.c functions
TriggerInfo_t ComparePix(MemImage_t * pic1, MemImage_t * pic2, char * DebugImgName);
// compare.c function
TriggerInfo_t ComparePix(MemImage_t * pic1, MemImage_t * pic2, int UpdateFatigue, int SkipFatigue, char * DebugImgName);


// jpeg2mem.c functions
MemImage_t * LoadJPEG(char* FileName, int scale_denom, int discard_colors, int ParseExif);
Expand Down
48 changes: 35 additions & 13 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,16 @@ int FollowDir = 0;
int ScaleDenom;
int SpuriousReject = 0;
int PostMotionKeep = 0;
int PreMotionKeep = 0;
int wait_close_write = 0;

int BrightnessChangeRestart = 1;
int BrightnessChangeRestart = 0;
int MotionFatigueTc = 30;

int FatigueSkipCount = 10; // Change to default zero.
int FatigueSkipCountdown = 0;
int FatigueGainPercent = 100;

char DiffMapFileName[200];
Regions_t Regions;

Expand Down Expand Up @@ -70,6 +75,7 @@ typedef struct {
int DiffMag;
int IsTimelapse;
int IsMotion;
int IsSkipFatigue;
}LastPic_t;

static LastPic_t LastPics[3];
Expand Down Expand Up @@ -142,15 +148,22 @@ static int ProcessImage(LastPic_t * New, int DeleteProcessed)
// compare with previous picture.
Trig.DiffLevel = Trig.x = Trig.y = 0;

int SkipFatigue = 0;
if (++FatigueSkipCountdown >= FatigueSkipCount){
FatigueSkipCountdown = 0;
SkipFatigue = 1;
}

if (LastPics[2].Image){
Trig = ComparePix(LastPics[1].Image, LastPics[0].Image, NULL);
Trig = ComparePix(LastPics[1].Image, LastPics[0].Image, 1, SkipFatigue, NULL);
}

if (Trig.DiffLevel >= Sensitivity && PixSinceDiff > 5 && Raspistill_restarted){
fprintf(Log,"Ignoring diff caused by raspistill restart\n");
Trig.DiffLevel = 0;
}
LastPics[0].DiffMag = Trig.DiffLevel;
LastPics[0].IsSkipFatigue = SkipFatigue;

if (FollowDir){
// When real-time following, the timestamp is more useful than the file name
Expand All @@ -174,30 +187,39 @@ static int ProcessImage(LastPic_t * New, int DeleteProcessed)

if (SpuriousReject && LastPics[2].Image &&
LastPics[0].IsMotion && LastPics[1].IsMotion
&& LastPics[2].DiffMag < (Sensitivity>>1)){
&& LastPics[2].DiffMag < Sensitivity/2){
// Compare to picture before last picture.
Trig = ComparePix(LastPics[2].Image, LastPics[0].Image, NULL);
Trig = ComparePix(LastPics[2].Image, LastPics[0].Image, 0, 1, NULL);

//printf("Diff with pix before last: %d\n",Trig.DiffLevel);
if (Trig.DiffLevel < Sensitivity){
// An event that was just one frame. We assume this was something
// spurious, like an insect or a rain drop
// spurious, like an insect or a rain drop or a camera glitch.
printf(" (spurious %d, ignore)", Trig.DiffLevel);
LastPics[0].IsMotion = 0;
LastPics[1].IsMotion = 0;
}
}
if (LastPics[0].IsMotion) fprintf(Log," (motion)");
if (LastPics[0].IsMotion){
fprintf(Log," (motion)");
if (LastPics[0].IsSkipFatigue) fprintf(Log, " (sf)");
}
if (LastPics[0].IsTimelapse) fprintf(Log," (time)");

if (LastPics[1].IsMotion) SinceMotionPix = 0;

if (SinceMotionPix <= PostMotionKeep+1 || LastPics[2].IsTimelapse){
// If it's motion, pre-motion, or timelapse, save it.
if (SaveDir[0]){

if (SaveDir[0]){
int KeepImage = 0;
if (LastPics[2].IsTimelapse) {KeepImage = 1; printf(" time"); }
if (LastPics[2].IsMotion) {KeepImage = 1; printf(" mot"); }
if (LastPics[1].IsMotion && PreMotionKeep) {KeepImage = 1; printf(" pre");}
if (SinceMotionPix < PostMotionKeep) {KeepImage = 1; printf(" post");}

if (KeepImage){
BackupImageFile(LastPics[2].Name, LastPics[2].DiffMag, 0);
}
}

if (LastPics[1].IsMotion) SinceMotionPix = 0;

fprintf(Log,"\n");
SinceMotionPix += 1;
Expand Down Expand Up @@ -540,7 +562,7 @@ int main(int argc, char **argv)

Log = stdout;

printf("Imgcomp version 0.9 (Nov 2018) by Matthias Wandel\n\n");
printf("Imgcomp version 0.95 (Jun 2020) by Matthias Wandel\n\n");

progname = argv[0];

Expand Down Expand Up @@ -650,7 +672,7 @@ int main(int argc, char **argv)
pic2 = LoadJPEG(argv[file_index+1], ScaleDenom, 0, 0);
if (pic1 && pic2){
Verbosity = 2;
ComparePix(pic1, pic2, "diff.ppm");
ComparePix(pic1, pic2, 0, 0,"diff.ppm");
}
free(pic1);
free(pic2);
Expand Down

0 comments on commit 2931614

Please sign in to comment.