diff --git a/Makefile b/Makefile index 305e21f..bdd334c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ APP = simpleskycam -include Makefile.rules -OBJECTS = gui.oo main.oo video.oo videoframe.oo videodev.oo filter.oo +OBJECTS = gui.oo main.oo video.oo videoframe.oo videodev.oo filter.oo detect.oo DISTNAME=simpleskycam-$(VERSION) ifeq ($(TARGET),) diff --git a/detect.cc b/detect.cc new file mode 100644 index 0000000..a9f9686 --- /dev/null +++ b/detect.cc @@ -0,0 +1,173 @@ + + +#include +#include +#include "config.h" +#include "gui.h" +#include "detect.h" + +extern Detect detect; + +// +// C / C++ Wrapper for the thread function +// +gpointer _DetectThread (gpointer data) { + detect.Thread (); + return NULL; +}; + + + +Detect::Detect() { // @suppress("Class members should be properly initialized") + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + + g_mutex_init (&mutexin); + g_mutex_init (&muteximage); + g_mutex_init (&mutex); + running = 1; + inFrame.SetSize(64, 64); + oldFrame.SetSize(64, 64); + image.SetSize(64, 64); + imagegtk.SetSize(64, 64); + thread = NULL; + thread = g_thread_new("Filter", _DetectThread, NULL); + objectW = 300; + objectH = 300; + posmaxx = 0; + posmaxy = 0; + maxx = NULL; + maxy = NULL; +}; + + + +Detect::~Detect() { + running = 0; + if (thread) { + g_thread_join (thread); + thread = NULL; + } +}; + + + +int Detect::NewFrame(VideoFrame *newframe) { + if (newframe == NULL) return -1; + LockInMutex(); + inFrame.CopyFrom(newframe); + inFrameNew = 1; + UnLockInMutex(); + return 0; +}; + + +// +// NewFrame: will set new frame +// Thread: newFrame |------> Find Object --- not found ---> send gui information +void Detect::Thread() { + DetectOutput output; + int posx, posy; + + while (running) { + // check for new frame + LockInMutex(); + if (inFrameNew == 1) { + inFrameNew = 0; + + // + // do some input detection improvement + InputDetect(&posx, &posy); + oldFrame.CopyFrom(&inFrame); + UnLockInMutex(); + + // copy output image for gtk + LockImageMutex(); + imagegtk.CopyFrom(&image); + UnLockImageMutex(); + output.posx = posx; + output.posy = posy; + output.image = &imagegtk; + gdk_threads_add_idle(cb_thread_detect , &output); + } + else + UnLockInMutex(); + + usleep (10000); + } +} + + +void Detect::SetInputSize (int nw, int nh) { + if (posmaxx < nw) { + if (maxx != NULL) free (maxx); + maxx = (float*) malloc (nw * sizeof (float)); + } + if (posmaxy < nh) { + if (maxy != NULL) free (maxy); + maxy = (float*) malloc (nh * sizeof (float)); + } + posmaxx = nw; + posmaxy = nh; +} + + +#define POWERVAL 10 +void Detect::InputDetect(int *posx, int *posy) { + int x, y, i, dx, dy, sy; + unsigned char *pxs, *pxi; + int idx, didx; + + image.SetSize (objectW, objectH); + SetInputSize(inFrame.w, inFrame.h); + pxs = inFrame.data; + pxi = image.data; + *posx = 0; + *posy = 0; + + // + // maximum brightness + for (x = 0; x < posmaxx; x++) maxx[x] = 0.0; + for (y = 0; y < posmaxy; y++) maxy[y] = 0.0; + for (x = 0; x < inFrame.w; x++) + for (y = 0; y < inFrame.h; y++) { + idx = 3*(inFrame.w * y + x); + maxx[x] += (pow(pxs[idx+0], POWERVAL) + pow(pxs[idx+1], POWERVAL) + pow(pxs[idx+2], POWERVAL)); + maxy[y] += (pow(pxs[idx+0], POWERVAL) + pow(pxs[idx+1], POWERVAL) + pow(pxs[idx+2], POWERVAL)); + } + // select maximum + for (x = 1; x < inFrame.w; x++) if (maxx[x] > maxx[*posx]) *posx = x; + for (y = 1; y < inFrame.h; y++) if (maxy[y] > maxy[*posy]) *posy = y; + + // select start corner + if (*posx < (objectW / 2)) x = 0; + else if (*posx + (objectW /2) > inFrame.w) x = inFrame.w - objectW; + else x = ((*posx) - (objectW / 2)); + + if (*posy < (objectH / 2)) sy = 0; + else if (*posy + (objectH / 2) > inFrame.h) sy = inFrame.h - objectH; + else sy = ((*posy) - (objectH / 2)); + + for (dx = 0; dx < image.w && x < inFrame.w; x++, dx++) + for (dy = 0, y = sy; dy < image.h && y < inFrame.h; y++, dy++) { + idx = 3* (inFrame.w * y + x); + didx = 3* (image.w * dy + dx); + // printf ("[%d , %d] --> [%d, %d] idx: %d --> %d Value:%d\n", x, y, dx, dy, idx, didx, pxs[idx+i]); + for (i = 0; i < 3; i++) pxi[didx+i] = pxs[idx+i]; + } +} + + +void Detect::SetObjectSize(int neww, int newh) { + LockImageMutex(); + LockInMutex(); + LockMutex(); + + objectH = newh; + objectW = neww; + + UnLockMutex(); + UnLockInMutex(); + UnLockImageMutex(); +} + + diff --git a/detect.h b/detect.h new file mode 100644 index 0000000..3b6af1c --- /dev/null +++ b/detect.h @@ -0,0 +1,77 @@ +/*************************************************************************************** + * + * detect.h is part of SimpleSkyCam. + * + *****************************************************************************************/ + + +#ifndef _DETECT_H_ +#define _DETECT_H_ + +#include "gui.h" +#include "config.h" +#include "video.h" +#include "videoframe.h" + + +struct { + VideoFrame *image; // detected image + int posx; // position of the detected object + int posy; // position of the detected object +} typedef DetectOutput; + + + +class Detect { +private: + int running; + VideoFrame inFrame; // input frame + VideoFrame oldFrame; // oldinput frame + int inFrameNew; // new input frame; + VideoFrame image; // output image + VideoFrame imagegtk; // output image -- send to gtk + GMutex muteximage; + GMutex mutexin; + GMutex mutex; // general mutex for changing settings + GThread *thread; + + float *maxx; + float *maxy; + int posmaxx; + int posmaxy; + + int objectW; // object Image Size Width + int objectH; // object Image Size Height + + void InputDetect(int *posx, int *posy); + void SetInputSize (int nw, int nh); + +public: + Detect(); + ~Detect(); + + int NewFrame(VideoFrame *newframe); + + // + // Thread Releated Functions (maybe soon private?) + int TryLockInMutex() { return g_mutex_trylock(&mutexin); }; + void LockInMutex() { g_mutex_lock(&mutexin);}; + void UnLockInMutex() { g_mutex_unlock(&mutexin);}; + + int TryLockImageMutex() { return g_mutex_trylock(&muteximage); }; + void LockImageMutex() { g_mutex_lock(&muteximage);}; + void UnLockImageMutex() { g_mutex_unlock(&muteximage);}; + + void LockMutex() { g_mutex_lock(&mutex);}; + void UnLockMutex() { g_mutex_unlock(&mutex);}; + + + // + // Object functions + void SetObjectSize (int neww, int newh); + void GetObjectPos (int *x, int *y); + + void Thread(); +}; + +#endif diff --git a/filter.cc b/filter.cc index 3ca1d28..4080ef0 100644 --- a/filter.cc +++ b/filter.cc @@ -29,9 +29,6 @@ Filter::Filter() { // @suppress("Class members should be properly initialized") g_mutex_init (&mutex); running = 1; inFrame.SetSize(64, 64); - oldFrame.SetSize(64, 64); - temp.SetSize(64, 64); - tempgtk.SetSize(64, 64); image.SetSize(64, 64); imagegtk.SetSize(64, 64); thread = NULL; @@ -50,7 +47,7 @@ Filter::~Filter() { -int Filter::NewFrame(VideoFrame *newframe) { +int Filter::NewFrame(VideoFrame *newframe, int posx, int posy) { if (newframe == NULL) return -1; LockInMutex(); inFrame.CopyFrom(newframe); @@ -60,40 +57,31 @@ int Filter::NewFrame(VideoFrame *newframe) { }; +void Filter::NewImage() { + newimage = 1; +} + + // // NewFrame: will set new frame // Thread: newFrame |------> Find Object --- not found ---> send gui information void Filter::Thread() { - int _run_once = 1; - while (running) { // check for new frame LockInMutex(); if (inFrameNew == 1) { inFrameNew = 0; - - // - // do some input detection improvement - InputDetect(); - oldFrame.CopyFrom(&inFrame); - UnLockInMutex(); - - // copy temporary image for gtk - LockTempMutex(); - tempgtk.CopyFrom(&temp); - UnLockTempMutex(); - gdk_threads_add_idle(cb_thread_filtertemp , &tempgtk); - // // do some other stuff use if possible only the oldFrame data ComposeOutput(); + UnLockInMutex(); // copy output image for gtk LockImageMutex(); imagegtk.CopyFrom(&image); UnLockImageMutex(); - gdk_threads_add_idle(cb_thread_filterimage , &imagegtk); + gdk_threads_add_idle(cb_thread_filter , &imagegtk); } else UnLockInMutex(); @@ -103,64 +91,30 @@ void Filter::Thread() { } -void Filter::InputDetect() { - int x, y; - float vo, v; - unsigned char *pxs, *pxso, *pxstemp; - unsigned char pixel; - int idx; - float fmin, fmax; - - temp.SetSize (inFrame.w, inFrame.h); - fi.SetSize (inFrame.w, inFrame.h); - pxs = inFrame.data; - pxso = oldFrame.data; - pxstemp = temp.data; - - for (x = 0; x < temp.w && x < inFrame.w && x < oldFrame.w; x++) - for (y = 0; y < temp.h && y < inFrame.h && y < oldFrame.h; y++) { - idx = (inFrame.w * y + x); - vo = pxso[3*idx+0] + pxso[3*idx+1] + pxso[3*idx+2]; - v = pxs[3*idx+0] + pxs[3*idx+1] + pxs[3*idx+2]; - fi.data[idx] = v - vo; - - if (idx == 0 || fmin > fi.data[idx]) fmin = fi.data[idx]; - if (idx == 0 || fmax < fi.data[idx]) fmax = fi.data[idx]; - } - - for (x = 0; x < temp.w && x < inFrame.w && x < oldFrame.w; x++) - for (y = 0; y < temp.h && y < inFrame.h && y < oldFrame.h; y++) { - idx = (inFrame.w * y + x); - pixel = (unsigned char)((float)( (fi.data[idx]-fmin) / (fmax-fmin)) * 256.0); - pxstemp[3*idx+0] = pixel; - pxstemp[3*idx+1] = pixel; - pxstemp[3*idx+2] = pixel; - } -} - - - #define FACTOR 25.0 void Filter::ComposeOutput() { int x, y, idx, i; float *pixd = NULL; // destination unsigned char *pixs = NULL; // source - image.SetSize(oldFrame.w, oldFrame.h); + image.SetSize(inFrame.w, inFrame.h); pixd = image.data; - pixs = oldFrame.data; + pixs = inFrame.data; if (pixd == NULL || pixs == NULL) return; - - for (x = 0; x < temp.w && x < inFrame.w && x < oldFrame.w; x++) - for (y = 0; y < temp.h && y < inFrame.h && y < oldFrame.h; y++) { - idx = 3 * (oldFrame.w * y + x); - - for (i = 0; i < 3; i++) - pixd[idx+i] = ((FACTOR-1) * pixd[idx+i]/FACTOR) + (pixs[idx+i] / FACTOR); - } + if (newimage) { + inFrame.CopyTo(&image); + newimage = 0; + } + else { + for (x = 0; x < inFrame.w; x++) + for (y = 0; y < inFrame.h; y++) { + idx = 3 * (inFrame.w * y + x); + + for (i = 0; i < 3; i++) + pixd[idx+i] = ((FACTOR-1) * pixd[idx+i]/FACTOR) + (pixs[idx+i] / FACTOR); + } + } } - - diff --git a/filter.h b/filter.h index d5898b8..ae67fbf 100644 --- a/filter.h +++ b/filter.h @@ -19,41 +19,30 @@ private: int running; VideoFrame inFrame; // input frame int inFrameNew; // new input frame; - VideoFrame oldFrame; // oldinput frame - VideoFrame temp; // temporary image - VideoFrame tempgtk; // temp image for gtk - FloatImage image; // output image + FloatImage image; // detected image VideoFrame imagegtk; // output image -- send to gtk GMutex muteximage; GMutex mutexin; GMutex mutextmp; // for access the temp image GMutex mutex; // general mutex for changing settings GThread *thread; - FloatImage fi; - - int objectW; // object Image Size Width - int objectH; // object Image Size Height float *fpixels; // + int newimage; - void InputDetect(); void ComposeOutput(); public: Filter(); ~Filter(); - int NewFrame (VideoFrame *newframe); - + int NewFrame (VideoFrame *newframe, int posx, int posy); + void NewImage (); // // Thread Releated Functions (maybe soon private?) - int TryLockInMutex() { return g_mutex_trylock(&mutextmp); }; - void LockInMutex() { g_mutex_lock(&mutextmp);}; - void UnLockInMutex() { g_mutex_unlock(&mutextmp);}; - - int TryLockTempMutex() { return g_mutex_trylock(&mutextmp); }; - void LockTempMutex() { g_mutex_lock(&mutextmp);}; - void UnLockTempMutex() { g_mutex_unlock(&mutextmp);}; + int TryLockInMutex() { return g_mutex_trylock(&mutexin); }; + void LockInMutex() { g_mutex_lock(&mutexin);}; + void UnLockInMutex() { g_mutex_unlock(&mutexin);}; int TryLockImageMutex() { return g_mutex_trylock(&muteximage); }; void LockImageMutex() { g_mutex_lock(&muteximage);}; diff --git a/gui.cc b/gui.cc index 37ecd63..e23ad07 100644 --- a/gui.cc +++ b/gui.cc @@ -14,12 +14,14 @@ #include "config.h" #include "video.h" #include "filter.h" +#include "detect.h" extern GtkBuilder *_builder_; // work around for threads extern Filter filter; +extern Detect detect; -GtkWidget *temp_da = NULL; -GdkPixbuf *temp_pixbuf = NULL; +GtkWidget *detect_da = NULL; +GdkPixbuf *detect_pixbuf = NULL; GtkWidget *image_da = NULL; GdkPixbuf *image_pixbuf = NULL; @@ -68,7 +70,7 @@ void displayerror (std::string error) { // // callback from filter thread. Data will point to the output VideoFrame. // Access to this data must be Locked before use. -gboolean cb_thread_filterimage (gpointer data) { +gboolean cb_thread_filter (gpointer data) { VideoFrame *vf = (VideoFrame *) data; int pix_h, pix_w; @@ -108,37 +110,37 @@ gboolean cb_thread_filterimage (gpointer data) { // callback from filter thread. Data will point to the temp VideoFrame. // this is propabely the object detection // Access to this data must be Locked before use and pointers must be looked to -gboolean cb_thread_filtertemp (gpointer data) { - VideoFrame *vf = (VideoFrame *) data; +gboolean cb_thread_detect (gpointer data) { + DetectOutput *dout = (DetectOutput *) data; int pix_h, pix_w; - if (temp_da == NULL) - temp_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "temp-da")); + if (detect_da == NULL) + detect_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "temp-da")); - if (vf == NULL) { - return false; - } + if (dout == NULL) return false; + if (dout->image == NULL) return false; - filter.LockTempMutex(); + detect.LockImageMutex(); - if (temp_pixbuf) { - pix_h = gdk_pixbuf_get_height(temp_pixbuf); - pix_w = gdk_pixbuf_get_width(temp_pixbuf); + if (detect_pixbuf) { + pix_h = gdk_pixbuf_get_height(detect_pixbuf); + pix_w = gdk_pixbuf_get_width(detect_pixbuf); } else pix_h = 0; - if (temp_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) { - if (temp_pixbuf != NULL) g_object_unref (temp_pixbuf); + if (detect_pixbuf == NULL || pix_h != dout->image->h || pix_w != dout->image->w) { + if (detect_pixbuf != NULL) g_object_unref (detect_pixbuf); - printf ("%s:%d %s New Pixbuf: %d x %d\n", __FILE__, __LINE__, __FUNCTION__, vf->w, vf->h); - temp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h); - pix_w = vf->w; - pix_h = vf->h; + printf ("%s:%d %s New Pixbuf: %d x %d\n", __FILE__, __LINE__, __FUNCTION__, dout->image->w, dout->image->h); + detect_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, dout->image->w, dout->image->h); + pix_w = dout->image->w; + pix_h = dout->image->h; } - videoframe_to_pixbuf(temp_pixbuf, vf); - gdk_window_invalidate_rect(gtk_widget_get_window(temp_da), NULL, true); + videoframe_to_pixbuf(detect_pixbuf, dout->image); + gdk_window_invalidate_rect(gtk_widget_get_window(detect_da), NULL, true); + filter.NewFrame(dout->image, dout->posx, dout->posy); - filter.UnLockTempMutex(); + detect.UnLockImageMutex(); return false; }; @@ -152,8 +154,8 @@ void cb_imagetempda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer da GdkPixbuf *pixbuf = NULL; GdkPixbuf *src = NULL; - if (area == temp_da) src = temp_pixbuf; - else if (area == image_da) src = image_pixbuf; + if (area == image_da) src = image_pixbuf; + else if (area == detect_da) src = detect_pixbuf; else return; clienth = gtk_widget_get_allocated_height(area); @@ -178,3 +180,22 @@ void cb_imagetempda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer da g_object_unref (pixbuf); }; + +void cb_detect_btnset (GtkWidget *widget, gpointer data) { + GtkWidget *txtx = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-x")); + GtkWidget *txty = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-y")); + GtkWidget *txtw = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-w")); + GtkWidget *txth = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-h")); + + detect.SetObjectSize( atoi(gtk_entry_get_text(GTK_ENTRY(txtw))), + atoi(gtk_entry_get_text(GTK_ENTRY(txtw)))); +}; + + +void cb_image_btnnew (GtkWidget *widget, gpointer data) { +}; + + +void cb_image_btnsave (GtkWidget *widget, gpointer data) { +}; + diff --git a/gui.h b/gui.h index 13e2fb8..3ba28f5 100644 --- a/gui.h +++ b/gui.h @@ -54,8 +54,14 @@ G_MODULE_EXPORT gboolean cb_thread_video (gpointer data); // // filter new temp or image data G_MODULE_EXPORT void cb_imagetempda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data); -G_MODULE_EXPORT gboolean cb_thread_filterimage (gpointer data); -G_MODULE_EXPORT gboolean cb_thread_filtertemp (gpointer data); +G_MODULE_EXPORT gboolean cb_thread_filter (gpointer data); +G_MODULE_EXPORT gboolean cb_thread_detect (gpointer data); +G_MODULE_EXPORT void cb_image_btnnew (GtkWidget *widget, gpointer data); +G_MODULE_EXPORT void cb_image_btnsave (GtkWidget *widget, gpointer data); + +// +// detection elements +G_MODULE_EXPORT void cb_detect_btnset (GtkWidget *widget, gpointer data); #ifdef __cplusplus } diff --git a/main.cc b/main.cc index af12239..c59def6 100644 --- a/main.cc +++ b/main.cc @@ -9,6 +9,7 @@ #include "config.h" #include "gui.h" #include "filter.h" +#include "detect.h" ////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -17,6 +18,7 @@ GtkBuilder *_builder_ = NULL; // work around for the thread situation Filter filter; +Detect detect; int main (int argc, char **argv) { GtkBuilder *builder; diff --git a/simpleskycam.ui b/simpleskycam.ui index dc4c50b..cf66afc 100644 --- a/simpleskycam.ui +++ b/simpleskycam.ui @@ -285,7 +285,7 @@ - + True True 4 @@ -305,7 +305,7 @@ - + True True 4 @@ -349,7 +349,7 @@ - + True True 4 @@ -367,7 +367,7 @@ - + True True 4 @@ -385,7 +385,7 @@ - + Auto True True @@ -402,7 +402,7 @@ - + Set Object True True @@ -413,6 +413,8 @@ 8 4 4 + + 5 @@ -534,11 +536,15 @@ True False - - button + + gtk-new True True True + True + True + + False @@ -547,11 +553,15 @@ - - button + + gtk-save True True True + True + True + + False diff --git a/video.cc b/video.cc index cf237ae..5e5512f 100644 --- a/video.cc +++ b/video.cc @@ -10,12 +10,13 @@ #include "gui.h" #include "video.h" #include "filter.h" +#include "detect.h" VideoDev videodev; GtkWidget *video_da = NULL; GdkPixbuf *video_pixbuf = NULL; extern GtkBuilder *_builder_; // work around for threads -extern Filter filter; +extern Detect detect; gboolean videoctrl_update(gpointer data) { GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid")); @@ -258,7 +259,7 @@ gboolean cb_thread_video (gpointer data) { pix_h = vf->h; } videoframe_to_pixbuf(video_pixbuf, vf); - filter.NewFrame(vf); + detect.NewFrame(vf); videodev.UnLockMutex(); } gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true); diff --git a/videoframe.cc b/videoframe.cc index b759477..f55a516 100644 --- a/videoframe.cc +++ b/videoframe.cc @@ -87,6 +87,20 @@ void VideoFrame::CopyFrom(FloatImage *source) { +void VideoFrame::CopyTo(FloatImage *dest) { + int x, y, i, idx; + + if (dest == NULL) return; + + dest->SetSize (w, h); + for (y = 0; y < h; y++) for (x = 0; x < w; x++) + for (i = 0; i < 3; i++) { + idx = 3 * (y * w + x); + dest->data[idx+i] = data[idx+i]; + } +} + + FloatImage::FloatImage() { data = NULL; @@ -136,6 +150,7 @@ FloatImage FloatImage::operator=(FloatImage rightside) { } + void FloatImage::CopyFrom(FloatImage *source) { SetSize (source->w, source->h); memcpy(data, source->data, 3 * w * h); diff --git a/videoframe.h b/videoframe.h index a9229ab..3ac6741 100644 --- a/videoframe.h +++ b/videoframe.h @@ -4,6 +4,7 @@ #include "gui.h" #include "config.h" + class FloatImage { public: int w; @@ -40,6 +41,7 @@ public: void SetH(int nh) { SetSize(w, nh); }; void CopyFrom(VideoFrame *source); void CopyFrom(FloatImage *source); + void CopyTo(FloatImage *dest); };