JeVoisBase  1.21
JeVois Smart Embedded Machine Vision Toolkit Base Modules
Share this page:
Loading...
Searching...
No Matches
FilterGPU.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
19
20// ####################################################################################################
21FilterGPU::FilterGPU(std::string const & instance) :
22 jevois::Component(instance), itsProgramChanged(false), itsQuadVertexBuffer(0), itsDisplay(EGL_NO_DISPLAY),
23 itsConfig(0), itsContext(0), itsSurface(0), itsFramebufferId(0), itsRenderbufferId(0),
24 itsRenderWidth(0), itsRenderHeight(0), itsRenderType(0)
25{ }
26
27// ####################################################################################################
28void FilterGPU::initDisplay()
29{
30 // Get an EGL display connection:
31 GL_CHECK(itsDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY));
32 if (itsDisplay == EGL_NO_DISPLAY) LFATAL("Could not get an OpenGL display");
33
34 // Initialize the EGL display connection:
35 EGLint major, minor;
36 GL_CHECK_BOOL(eglInitialize(itsDisplay, &major, &minor););
37 LINFO("Initialized OpenGL-ES with EGL v" << major << '.' << minor);
38
39 // Get an appropriate EGL configuration:
40 EGLint num_config;
41 static EGLint const cfg_attr[] =
42 { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE };
43 GL_CHECK_BOOL(eglChooseConfig(itsDisplay, cfg_attr, &itsConfig, 1, &num_config));
44 if (num_config < 1) LFATAL("Could not find a suitable OpenGL config");
45
46 // Create a pbuffer surface:
47 GL_CHECK(itsSurface = eglCreatePbufferSurface(itsDisplay, itsConfig, NULL));
48
49 // Bind to OpenGL-ES API:
50 GL_CHECK_BOOL(eglBindAPI(EGL_OPENGL_ES_API));
51
52 // Create an EGL rendering context:
53 static EGLint const ctx_attr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
54 GL_CHECK(itsContext = eglCreateContext(itsDisplay, itsConfig, EGL_NO_CONTEXT, ctx_attr));
55 if (itsContext == EGL_NO_CONTEXT) LFATAL("Failed to create OpenGL context");
56
57 // Bind the context to the surface:
58 GL_CHECK(eglMakeCurrent(itsDisplay, itsSurface, itsSurface, itsContext));
59}
60
61// ####################################################################################################
63{
64 // Kill our member variables before we close down OpenGL:
65 itsProgram.reset(); itsSrcTex.reset();
66
67 if (itsRenderbufferId) glDeleteRenderbuffers(1, &itsRenderbufferId);
68 if (itsFramebufferId) glDeleteFramebuffers(1, &itsFramebufferId);
69
70 glDeleteBuffers(1, &itsQuadVertexBuffer);
71
72 // Delete surface, context, etc and close down:
73 eglMakeCurrent(itsDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
74 if (itsSurface) eglDestroySurface(itsDisplay, itsSurface);
75 eglDestroyContext(itsDisplay, itsContext);
76 eglTerminate(itsDisplay);
77}
78
79// ####################################################################################################
80void FilterGPU::setProgram(std::string const & vertex_shader, std::string const & frag_shader)
81{
82 // We cannot load the program here as our display may not have been initialized yet, and we may be in a different
83 // thread. Let's just remember the file names and we will do the work in process():
84 std::lock_guard<std::mutex> _(itsMutex);
85 itsVshader = absolutePath(vertex_shader);
86 itsFshader = absolutePath(frag_shader);
87 itsProgramChanged = true;
88 itsProgramParams.clear();
89}
90
91// ####################################################################################################
92void FilterGPU::setProgramParam2f(std::string name, float val1, float val2)
93{
94 std::lock_guard<std::mutex> _(itsMutex);
95 itsProgramParams[name] = { F2, { val1, val2 } };
96 itsProgramChanged = true;
97}
98
99// ####################################################################################################
100void FilterGPU::setProgramParam1f(std::string name, float val)
101{
102 std::lock_guard<std::mutex> _(itsMutex);
103 itsProgramParams[name] = { F1, { val, 0.0F } };
104 itsProgramChanged = true;
105}
106
107// ####################################################################################################
108void FilterGPU::setProgramParam2i(std::string name, int val1, int val2)
109{
110 std::lock_guard<std::mutex> _(itsMutex);
111 itsProgramParams[name] = { I2, { float(val1), float(val2) } };
112 itsProgramChanged = true;
113}
114
115// ####################################################################################################
116void FilterGPU::setProgramParam1i(std::string name, int val)
117{
118 std::lock_guard<std::mutex> _(itsMutex);
119 itsProgramParams[name] = { I2, { float(val), 0.0F } };
120 itsProgramChanged = true;
121}
122
123// ####################################################################################################
124void FilterGPU::process(cv::Mat const & src, cv::Mat & dst)
125{
126 // We init the display here so that it is in the same thread as the subsequent processing, as OpenGL is not very
127 // thread-friendly. Yet, see here for an alternative, which is basically to create some sort of shadow context in the
128 // process() thread after having created the context in the constructor or init() thread:
129 // http://stackoverflow.com/questions/11726650/egl-can-context-be-shared-between-threads
130 if (itsDisplay == EGL_NO_DISPLAY) initDisplay();
131
132 if (src.type() != CV_8UC1 && src.type() != CV_8UC4) LFATAL("Source pixel format must be CV_8UC1 or CV_8UC4");
133 if (dst.type() != CV_8UC2 && dst.type() != CV_8UC4) LFATAL("Dest pixel format must be CV_8UC2 or CV_8UC4");
134 GLuint const srcformat = (src.channels() == 4 ? GL_RGBA : GL_LUMINANCE); // GL_ALPHA also works
135 GLuint const dstformat = (dst.channels() == 4 ? GL_RGBA4 : GL_RGB565);
136
137 // Allocate our textures if needed, eg, first time we are called or sizes have changed:
138 if (!itsSrcTex || itsSrcTex->Width != src.cols || itsSrcTex->Height != src.rows || itsSrcTex->Format != srcformat)
139 {
140 itsSrcTex.reset(new GPUtexture(src.cols, src.rows, srcformat, false));
141 LINFO("Input texture " << itsSrcTex->Width << 'x' << itsSrcTex->Height << ' ' <<
142 (srcformat == GL_RGBA ? "RGBA" : "LUMINANCE") << " ready.");
143 }
144
145 // Create our framebuffer and renderbuffer if needed:
146 if (itsRenderbufferId == 0 || int(itsRenderWidth) != dst.cols || int(itsRenderHeight) != dst.rows ||
147 itsRenderType != dst.type())
148 {
149 if (itsRenderbufferId) glDeleteRenderbuffers(1, &itsRenderbufferId);
150 if (itsFramebufferId) glDeleteFramebuffers(1, &itsFramebufferId);
151
152 GL_CHECK(glGenFramebuffers(1, &itsFramebufferId));
153 GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, itsFramebufferId));
154
155 GL_CHECK(glGenRenderbuffers(1, &itsRenderbufferId));
156 GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, itsRenderbufferId));
157 GL_CHECK(glRenderbufferStorage(GL_RENDERBUFFER, dstformat, dst.cols, dst.rows));
158 GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, itsRenderbufferId));
159
160 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
161 LFATAL("Framebuffer creation failed");
162
163 itsRenderWidth = dst.cols; itsRenderHeight = dst.rows; itsRenderType = dst.type();
164 LINFO("Render buffer " << itsRenderWidth << 'x' << itsRenderHeight << ' ' <<
165 (dst.channels() == 2 ? "RGB565" : "RGBA") << " ready.");
166 }
167
168 // Create our vertex buffer if needed:
169 if (itsQuadVertexBuffer == 0)
170 {
171 // Create an ickle vertex buffer:
172 static GLfloat const qv[] =
173 { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
174
175 GL_CHECK(glGenBuffers(1, &itsQuadVertexBuffer));
176 glBindBuffer(GL_ARRAY_BUFFER, itsQuadVertexBuffer);
177 glBufferData(GL_ARRAY_BUFFER, sizeof(qv), qv, GL_STATIC_DRAW);
178 GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0));
179 }
180
181 // Set background color and clear buffers:
182 glClearColor(0.15f, 0.25f, 0.35f, 1.0f);
183 glClear(GL_COLOR_BUFFER_BIT);
184
185 // Copy source pixel data to source texture:
186 itsSrcTex->setPixels(src.data);
187
188 // Tell OpenGL to render into our destination framebuffer:
189 GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, itsFramebufferId));
190 GL_CHECK(glViewport(0, 0, itsRenderWidth, itsRenderHeight));
191
192 // Load the shader program if needed:
193 if (itsProgramChanged)
194 {
195 std::lock_guard<std::mutex> _(itsMutex);
196
197 // Nuke any old program first:
198 itsProgram.reset();
199
200 // Create, load and compile the program:
201 itsProgram.reset(new GPUprogram(itsVshader.c_str(), itsFshader.c_str()));
202 itsProgramChanged = false;
203
204 // Tell OpenGL to use our program:
205 glUseProgram(itsProgram->id());
206
207 // Set all the parameters:
208 GLuint i = itsProgram->id();
209 for (auto const & p : itsProgramParams)
210 {
211 char const * n = p.first.c_str();
212 float const * v = &p.second.val[0];
213 switch (p.second.type)
214 {
215 case F2: GL_CHECK(glUniform2f(glGetUniformLocation(i, n), v[0], v[1])); break;
216 case F1: GL_CHECK(glUniform1f(glGetUniformLocation(i, n), v[0])); break;
217 case I2: GL_CHECK(glUniform2i(glGetUniformLocation(i, n), int(v[0]), int(v[1]))); break;
218 case I1: GL_CHECK(glUniform1i(glGetUniformLocation(i, n), int(v[0]))); break;
219 default: LFATAL("Unsupported GPU program parameter type " << p.second.type);
220 }
221 }
222 }
223
224 if (!itsProgram) LFATAL("You need to set a program before processing frames");
225
226 // Set program parameters that all programs should always use, ignore any error:
227 glUniform2f(glGetUniformLocation(itsProgram->id(), "texelsize"), 1.0f / dst.cols, 1.0f / dst.rows);
228 glUniform1i(glGetUniformLocation(itsProgram->id(), "tex"), 0);
229
230 // Draw the texture onto the triangle strip, applying the program:
231 GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, itsQuadVertexBuffer));
232 GL_CHECK(glActiveTexture(GL_TEXTURE0));
233 GL_CHECK(glBindTexture(GL_TEXTURE_2D, itsSrcTex->Id));
234
235 GLuint loc = glGetAttribLocation(itsProgram->id(), "vertex");
236 GL_CHECK(glVertexAttribPointer(loc, 4, GL_FLOAT, 0, 16, 0));
237 GL_CHECK(glEnableVertexAttribArray(loc));
238 GL_CHECK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
239
240 // Copy the rendered pixels to destination image:
241 GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, itsFramebufferId));
242 if (dstformat == GL_RGBA4) GL_CHECK(glReadPixels(0, 0, dst.cols, dst.rows, GL_RGBA, GL_UNSIGNED_BYTE, dst.data));
243 else GL_CHECK(glReadPixels(0, 0, dst.cols, dst.rows, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, dst.data));
244 GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
245}
246
#define GL_CHECK(stmt)
#define GL_CHECK_BOOL(stmt)
~FilterGPU()
Destructor.
Definition FilterGPU.C:62
void setProgramParam2f(std::string name, float val1, float val2)
Set some program parameters, 2 float version.
Definition FilterGPU.C:92
void setProgramParam1i(std::string name, int val)
Set some program parameters, 1 int version.
Definition FilterGPU.C:116
void setProgram(std::string const &vertex_shader, std::string const &frag_shader)
Set an image processing program.
Definition FilterGPU.C:80
void setProgramParam1f(std::string name, float val)
Set some program parameters, 1 float version.
Definition FilterGPU.C:100
void setProgramParam2i(std::string name, int val1, int val2)
Set some program parameters, 2 int version.
Definition FilterGPU.C:108
FilterGPU(std::string const &instance)
Constructor.
Definition FilterGPU.C:21
void process(cv::Mat const &src, cv::Mat &dst)
Process an image. The dst image should be allocated with correct image size and pixel type.
Definition FilterGPU.C:124
Simple class to load and compile some OpenGL-ES program.
Definition GPUprogram.H:24
Simple class to hold an OpenGL texture.
Definition GPUtexture.H:27
std::filesystem::path absolutePath(std::filesystem::path const &path="")
#define LFATAL(msg)
#define LINFO(msg)