first experiment with object detection

test16bit
Steffen Pohle 4 years ago
parent 874796b87f
commit 32a1fd8f54

@ -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),)

@ -0,0 +1,173 @@
#include <unistd.h>
#include <math.h>
#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();
}

@ -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

@ -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);
}
}
}

@ -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);};

@ -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) {
};

10
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
}

@ -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;

@ -285,7 +285,7 @@
</packing>
</child>
<child>
<object class="GtkEntry">
<object class="GtkEntry" id="detect-entry-x">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-left">4</property>
@ -305,7 +305,7 @@
</packing>
</child>
<child>
<object class="GtkEntry">
<object class="GtkEntry" id="detect-entry-w">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-left">4</property>
@ -349,7 +349,7 @@
</packing>
</child>
<child>
<object class="GtkEntry">
<object class="GtkEntry" id="detect-entry-y">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-left">4</property>
@ -367,7 +367,7 @@
</packing>
</child>
<child>
<object class="GtkEntry">
<object class="GtkEntry" id="detect-entry-h">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-left">4</property>
@ -385,7 +385,7 @@
</packing>
</child>
<child>
<object class="GtkCheckButton" id="detect_cbauto">
<object class="GtkCheckButton" id="detect-cb-auto">
<property name="label" translatable="yes">Auto</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
@ -402,7 +402,7 @@
</packing>
</child>
<child>
<object class="GtkButton" id="btnSet">
<object class="GtkButton" id="detect-btnset">
<property name="label" translatable="yes">Set Object</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
@ -413,6 +413,8 @@
<property name="margin-end">8</property>
<property name="margin-top">4</property>
<property name="margin-bottom">4</property>
<signal name="activate" handler="cb_detect_btnset" swapped="no"/>
<signal name="pressed" handler="cb_detect_btnset" swapped="no"/>
</object>
<packing>
<property name="left-attach">5</property>
@ -534,11 +536,15 @@
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">button</property>
<object class="GtkButton" id="btn-img-new">
<property name="label">gtk-new</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-stock">True</property>
<property name="always-show-image">True</property>
<signal name="activate" handler="cb_image_btnnew" swapped="no"/>
<signal name="pressed" handler="cb_image_btnnew" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@ -547,11 +553,15 @@
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">button</property>
<object class="GtkButton" id="btn-img-save">
<property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-stock">True</property>
<property name="always-show-image">True</property>
<signal name="activate" handler="cb_image_btnsave" swapped="no"/>
<signal name="pressed" handler="cb_image_btnsave" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>

@ -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);

@ -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);

@ -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);
};

Loading…
Cancel
Save