37#define WEIGHT_SCALEBITS ((env_size_t) 8)
57 if (tagName[2] ==
'd') { offset = 0; bitshift = 6; }
58 else { offset = (std::atoi(tagName + 10) + 6) * onechan; bitshift = 4; }
60 case 'b': offset = onechan; bitshift = 6;
break;
61 case 'i': offset = 2 * onechan; bitshift = 7;
break;
62 case 's': offset = (std::atoi(tagName + 10) + 2) * onechan; bitshift = 0;
break;
63 case 'f': offset = 7 * onechan; bitshift = 5;
break;
64 default:
LFATAL(
"Unknown channel " << tagName);
72 if (offset + 16 > vd->
gist_size)
LFATAL(
"gist offset " << offset <<
" out of range");
82 jevois::Component(instance), gist_size(72 * 16), itsProfiler(
"Saliency", 100, LOG_DEBUG), itsInputDone(true)
88 env_img_init_empty(&prev_input);
102 itsVisitorData.
envp = &envp;
117 if (!env_img_initialized(chanOut))
return;
119 intg32*
const sptr = env_img_pixelsw(chanOut);
123 std::lock_guard<std::mutex> _(itsMtx);
125 if (!env_img_initialized(result))
128 intg32*
const dptr = env_img_pixelsw(result);
138 intg32*
const dptr = env_img_pixelsw(result);
148#define SALUPDATE(envval, param) \
149 prev = envp.envval; envp.envval = saliency::param::get(); if (envp.envval != prev) nuke = true;
152void Saliency::processStart(
struct env_dims const & dims,
bool do_gist)
156 std::unique_lock<std::mutex> ulck(itsRawImageMtx);
157 itsInputDone =
false;
160 bool nuke =
false;
size_t prev;
197 if (dims.
w < 32 || dims.
h < 32)
LFATAL(
"input dims " << dims.
w <<
'x' << dims.
h <<
" too small -- REJECTED");
198 if (dims.
w > 2048 || dims.
h > 2048)
LFATAL(
"input dims " << dims.
w <<
'x' << dims.
h <<
" too large -- REJECTED");
201 if (env_img_initialized(&prev_input) && (prev_input.
dims.
w != dims.
w || prev_input.
dims.
h != dims.
h)) nuke =
true;
219 std::unique_lock<std::mutex> ulck(itsRawImageMtx);
220 if (itsInputDone)
return;
221 itsRawImageCond.wait(ulck, [&]() {
return itsInputDone; } );
228 static void * statdata =
nullptr;
233 processStart(dims, do_gist);
262 std::future<void> colorfut;
274 itsInputDone =
true; itsRawImageCond.notify_all();
281 std::future<void> motfut;
284 env_mt_motion_channel_input(&motion_chan,
"motion", bwimg.
dims, &lowpass5, statfunc, statdata, &
motion);
288 std::future<void> orifut;
291 env_mt_chan_orientation(
"orientation", &bwimg, statfunc, statdata, &
ori);
295 std::future<void> flickfut;
326 if (statfunc) (*statfunc)(statdata,
"saliency", &
salmap);
344 static void * statdata =
nullptr;
349 processStart(dims, do_gist);
353 const intg32 lumthresh = (3*255) / 10;
358 int const nthreads = 4;
359 int hh = dims.
h / nthreads;
360 std::vector<std::future<void> > rgbyfut;
361 unsigned char const * inpix = input.
pixels<
unsigned char>();
362 intg32 * rgpix = env_img_pixelsw(&rgimg);
363 intg32 * bypix = env_img_pixelsw(&byimg);
364 intg32 * bwpix = env_img_pixelsw(&bwimg);
365 for (
int i = 0; i < nthreads-1; ++i)
367 int offset = dims.w * hh * ii;
368 convertYUYVtoRGBYL(dims.w, hh, inpix + offset*2, rgpix + offset, bypix + offset, bwpix + offset,
369 lumthresh, imath.nbits);
373 int offset = dims.
w * hh * (nthreads - 1);
374 convertYUYVtoRGBYL(dims.
w, dims.
h - hh * (nthreads-1), inpix + offset*2, rgpix + offset, bypix + offset,
375 bwpix + offset, lumthresh, imath.
nbits);
384 std::future<void> rgfut, byfut;
388 for (
auto & f : rgbyfut) f.get();
393 itsInputDone =
true; itsRawImageCond.notify_all();
422 std::future<void> motfut;
425 env_mt_motion_channel_input(&motion_chan,
"motion", bwimg.
dims, &lowpass5, statfunc, statdata, &
motion);
429 std::future<void> orifut;
432 env_mt_chan_orientation(
"orientation", &bwimg, statfunc, statdata, &
ori);
436 std::future<void> flickfut;
460 if (rgfut.valid()) rgfut.get();
468 const intg32 *
const byptr = env_img_pixels(&byOut);
471 for (
env_size_t i = 0; i < sz; ++i) dptr[i] = (dptr[i] + byptr[i]) >> 1;
475 if (statfunc) (*statfunc)(statdata,
"color", &
color);
483 if (orifut.valid()) orifut.get();
486 if (flickfut.valid()) flickfut.get();
489 if (motfut.valid()) motfut.get();
495 if (statfunc) (*statfunc)(statdata,
"saliency", &
salmap);
511void Saliency::env_mt_chan_orientation(
const char* tagName,
const struct env_image* img,
524 's',
't',
'e',
'e',
'r',
'a',
'b',
'l',
'e',
526 '/',
'_',
'_',
')',
'\0'
534 std::vector<std::future<void> > fut;
538 struct env_image chanOut; env_img_init_empty(&chanOut);
540 char tagname[17]; memcpy(tagname, buf, 17);
541 tagname[10] =
'0' + ((ii+1) / 10);
542 tagname[11] =
'0' + ((ii+1) % 10);
549 status_func, status_userdata, &chanOut);
552 std::lock_guard<std::mutex>
_(mtx);
553 if (!env_img_initialized(result))
557 env_img_pixelsw(result));
569 for (std::future<void> & f : fut) f.get();
573 if (env_img_initialized(result))
576 if (status_func) (*status_func)(status_userdata, tagName,
result);
580void Saliency::env_mt_motion_channel_input(
struct env_motion_channel* chan,
const char* tagName,
597 'r',
'e',
'i',
'c',
'h',
'a',
'r',
'd',
't',
599 '/',
'_',
'_',
')',
'\0'
608 std::vector<std::future<void> > fut;
612 struct env_image chanOut; env_img_init_empty(&chanOut);
614 char tagname[17]; memcpy(tagname, buf, 17);
615 tagname[10] =
'0' + ((d+1) / 10);
616 tagname[11] =
'0' + ((d+1) % 10);
629 for (
env_size_t i = firstlevel; i < depth; ++i)
637 &chan->
shifted_prev[d], &shiftedCur, status_func, status_userdata, &chanOut);
643 std::lock_guard<std::mutex>
_(mtx);
644 if (env_img_initialized(&chanOut))
646 if (!env_img_initialized(result))
650 env_img_pixelsw(result));
663 for (std::future<void> & f : fut) f.get();
665 if (env_img_initialized(result))
668 if (status_func) (*status_func)(status_userdata, tagName,
result);
674 if (env_img_initialized(&
salmap) ==
false)
LFATAL(
"Saliency map has not yet been computed");
680 for (
int j = 0; j < smh; ++j)
681 for (
int i = 0; i < smw; ++i)
682 if (*sm > value) { value = *sm++; x = i; y = j; }
else ++sm;
688 if (env_img_initialized(&
salmap) ==
false)
LFATAL(
"Saliency map has not yet been computed");
691 float const sigsq = sigma * sigma;
693 for (
int j = 0; j < smh; ++j)
694 for (
int i = 0; i < smw; ++i)
696 float const distsq = (i-x)*(i-x) + (j-y)*(j-y);
702 val *= 1.0F - expf( -0.5F * (distsq - sigsq ) / sigsq);
703 *sm++ =
static_cast<intg32>(val + 0.4999F);
712 unsigned int const imgw = img.
width;
713 unsigned short * d = img.
pixelsw<
unsigned short>() + xoff + yoff * imgw;
720 unsigned short const * dd = d;
726 unsigned short const val = 0x8000 | v;
727 for (
env_size_t k = 0; k < scale; ++k) *d++ = val;
732 for (
env_size_t k = 1; k < scale; ++k) { memcpy(d, dd, ws * 2); d += imgw; }
741 unsigned int scale,
unsigned int bitshift)
743 unsigned int const imgw = img.
width;
744 unsigned short * d = img.
pixelsw<
unsigned short>() + xoff + yoff * imgw;
751 unsigned short const * dd = d;
756 intg32 v = (*s++) >> bitshift;
if (v > 255) v = 255;
757 unsigned short const val = 0x8000 | v;
758 for (
env_size_t k = 0; k < scale; ++k) *d++ = val;
763 for (
env_size_t k = 1; k < scale; ++k) { memcpy(d, dd, ws * 2); d += imgw; }
772 unsigned int yoff,
unsigned int width,
unsigned int scale)
774 unsigned int const height = gistsize / width;
775 unsigned int const imgw = img.
width;
776 unsigned short * d = img.
pixelsw<
unsigned short>() + xoff + yoff * imgw;
777 unsigned char const *
const dataend = gist + gistsize;
778 unsigned int const ws = width * scale;
782 unsigned short const * dd = d;
787 intg32 v = gist >= dataend ? 0 : *gist++;
788 unsigned short const val = 0x8000 | v;
789 for (
env_size_t k = 0; k < scale; ++k) *d++ = val;
794 for (
env_size_t k = 1; k < scale; ++k) { memcpy(d, dd, ws * 2); d += imgw; }
#define JEVOIS_UNUSED_PARAM(x)
#define JEVOIS_WAIT_GET_FUTURE(f)
void drawGist(jevois::RawImage &img, unsigned char const *gist, size_t gistsize, unsigned int xoff, unsigned int yoff, unsigned int width, unsigned int scale)
#define SALUPDATE(envval, param)
void drawMap(jevois::RawImage &img, env_image const *fmap, unsigned int xoff, unsigned int yoff, unsigned int scale)
unsigned char * gist
Gist vector has 1152 entries, 72 feature maps * 16 values/map.
void inhibitionOfReturn(int const x, int const y, float const sigma)
Inhibit the saliency map around a point, sigma is in pixels at the sacle of the map.
struct env_image salmap
The saliency map.
void process(jevois::RawImage const &input, bool do_gist)
Process a raw YUYV image. Results are stored in the Saliency class.
void getSaliencyMax(int &x, int &y, intg32 &value)
Get location and value of max point in the saliency map.
Saliency(std::string const &instance)
Constructor.
void waitUntilDoneWithInput() const
Wait until process() is done using the input image.
virtual ~Saliency()
Destructor.
void checkpoint(char const *description)
void env_c_luminance_from_byte(const struct env_rgb_pixel *const src, const env_size_t sz, const env_size_t nbits, intg32 *const dst)
get the luminance with nbits of precision of the input image
void env_c_image_div_scalar_accum(const intg32 *const a, const env_size_t sz, intg32 val, intg32 *const dst)
result += a / val
void env_c_image_div_scalar(const intg32 *const a, const env_size_t sz, intg32 val, intg32 *const dst)
result = a / val
void env_chan_intensity(const char *tagName, const struct env_params *envp, const struct env_math *imath, const struct env_dims inputdims, const struct env_pyr *lowpass5, const int normalizeOutput, env_chan_status_func *status_func, void *status_userdata, struct env_image *result)
An intensity channel.
void env_chan_direction(const char *tagName, const struct env_params *envp, const struct env_math *imath, const struct env_dims inputdims, const struct env_pyr *unshiftedPrev, const struct env_pyr *unshiftedCur, const struct env_pyr *shiftedPrev, const struct env_pyr *shiftedCur, env_chan_status_func *status_func, void *status_userdata, struct env_image *result)
A motion sensitive channel with direction selectivity.
void env_chan_flicker(const char *tagName, const struct env_params *envp, const struct env_math *imath, const struct env_image *prev, const struct env_image *cur, env_chan_status_func *status_func, void *status_userdata, struct env_image *result)
A temporal flicker channel.
void env_chan_color(const char *tagName, const struct env_params *envp, const struct env_math *imath, const struct env_rgb_pixel *const colimg, const struct env_dims dims, env_chan_status_func *status_func, void *status_userdata, struct env_image *result)
A double opponent color channel that combines r/g, b/y subchannels.
void env_chan_steerable(const char *tagName, const struct env_params *envp, const struct env_math *imath, const struct env_dims inputdims, const struct env_pyr *hipass9, const env_size_t thetaidx, env_chan_status_func *status_func, void *status_userdata, struct env_image *result)
An orientation filtering channel.
void env_chan_msflicker(const char *tagName, const struct env_params *envp, const struct env_math *imath, const struct env_dims inputDims, const struct env_pyr *prev_lowpass5, const struct env_pyr *cur_lowpass5, env_chan_status_func *status_func, void *status_userdata, struct env_image *result)
A true multi-scale temporal flicker channel.
void() env_chan_status_func(void *userdata, const char *tagName, const struct env_image *img)
void env_img_swap(struct env_image *img1, struct env_image *img2)
void env_img_resize_dims(struct env_image *img, const struct env_dims d)
#define env_img_initializer
void env_img_make_empty(struct env_image *img)
void env_img_init(struct env_image *img, const struct env_dims d)
void env_grid_average(const struct env_image *src, unsigned char *dest, unsigned int bitshift, env_size_t nx, env_size_t ny)
Compute average values in each tile of a grid.
void env_pyr_build_hipass_9(const struct env_image *image, env_size_t firstlevel, const struct env_math *imath, struct env_pyr *result)
void env_pyr_build_lowpass_5(const struct env_image *image, env_size_t firstlevel, const struct env_math *imath, struct env_pyr *result)
Wrapper for _cpu or _cuda version.
void env_max_normalize_inplace(struct env_image *src, intg32 min, intg32 max, enum env_maxnorm_type typ, const intg32 rangeThresh)
void env_shift_image(const struct env_image *srcImg, const env_ssize_t dxnumer, const env_ssize_t dynumer, const env_size_t denombits, struct env_image *result)
void env_init_integer_math(struct env_math *imath, const struct env_params *envp)
void env_motion_channel_init(struct env_motion_channel *chan, const struct env_params *envp)
void env_motion_channel_destroy(struct env_motion_channel *chan)
intg32 env_total_weight(const struct env_params *envp)
void env_params_set_defaults(struct env_params *envp)
void env_params_validate(const struct env_params *envp)
env_size_t env_max_pyr_depth(const struct env_params *envp)
void env_pyr_init(struct env_pyr *pyr, const env_size_t n)
Construct with a given number of empty images.
void env_pyr_swap(struct env_pyr *pyr1, struct env_pyr *pyr2)
Swap contents with another env_pyr.
void env_pyr_copy_src_dst(const struct env_pyr *src, struct env_pyr *dst)
void env_pyr_make_empty(struct env_pyr *dst)
ENV_INTG32_TYPE intg32
32-bit signed integer
void drawRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int thick, unsigned int col)
void convertYUYVtoRGBYL(unsigned int w, unsigned int h, unsigned char const *src, int *dstrg, int *dstby, int *dstlum, int thresh, int inputbits)
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
A simple struct to hold a pair of width/height dimensions.
A composite channel containing a set of direction channels.
env_size_t num_directions
struct env_pyr unshifted_prev
struct env_pyr * shifted_prev
int(* submapPreProc)(const char *tagName, env_size_t clev, env_size_t slev, struct env_image *submap, const struct env_image *center, const struct env_image *surround, void *user_data)
env_size_t num_motion_directions
enum env_maxnorm_type maxnorm_type
env_size_t num_orientations
number of Gabor subchannels
This class implements a set of images, often used as a dyadic pyramid.