Mandelbrot Slicing Image to Improve Speed - c++

The Mandelbrot set currently displays the image in one whole set by calling the function from the main.
// This shows the whole set.
compute_mandelbrot(-2.0, 1.0, 1.125, -1.125);
My plan is to split the image up into 16 horizontal slices and then display it to improve the speed as can then parallel program this in.
I'm unsure how to create these slices, can someone explain, redirect me or show some example code
image details:
// The size of the image to generate.
const int WIDTH = 100;
const int HEIGHT = 100;
// The number of times to iterate before we assume that a point isn't in the
// Mandelbrot set.
const int MAX_ITERATIONS = 500;
For the purpose of testing, ill send the full code, there are no errors - it is not coded efficiently evidently as the whole process takes over 30 seconds to output, which is way too long for a Mandelbrot set, hence the urgency of the slicing and parallel programming.
If anyone has any other pointers then they would be greatly appreciated
e.g. where to implement parallel programming
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::complex;
using std::cout;
using std::endl;
using std::ofstream;
// Define the alias "the_clock" for the clock type we're going to use.
typedef std::chrono::steady_clock the_clock;
// The size of the image to generate.
const int WIDTH = 100;
const int HEIGHT = 100;
// The number of times to iterate before we assume that a point isn't in the
// Mandelbrot set.
const int MAX_ITERATIONS = 500;
// The image data.
// Each pixel is represented as 0xRRGGBB.
uint32_t image[HEIGHT][WIDTH];
// Write the image to a TGA file with the given name.
// Format specification: http://www.gamers.org/dEngine/quake3/TGA.txt
void write_tga(const char *filename)
{
ofstream outfile(filename, ofstream::binary);
uint8_t header[18] = {
0, // no image ID
0, // no colour map
2, // uncompressed 24-bit image
0, 0, 0, 0, 0, // empty colour map specification
0, 0, // X origin
0, 0, // Y origin
WIDTH & 0xFF, (WIDTH >> 8) & 0xFF, // width
HEIGHT & 0xFF, (HEIGHT >> 8) & 0xFF, // height
24, // bits per pixel
0, // image descriptor
};
outfile.write((const char *)header, 18);
for (int y = 0; y < HEIGHT; ++y)
{
for (int x = 0; x < WIDTH; ++x)
{
uint8_t pixel[3] = {
image[y][x] & 0xFF, // blue channel
(image[y][x] >> 8) & 0xFF, // green channel
(image[y][x] >> 16) & 0xFF, // red channel
};
outfile.write((const char *)pixel, 3);
}
}
outfile.close();
if (!outfile)
{
// An error has occurred at some point since we opened the file.
cout << "Error writing to " << filename << endl;
exit(1);
}
}
// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot(double left, double right, double top, double bottom)
{
for (int y = 0; y < HEIGHT; ++y)
{
for (int x = 0; x < WIDTH; ++x)
{
// Work out the point in the complex plane that
// corresponds to this pixel in the output image.
complex<double> c(left + (x * (right - left) / WIDTH),
top + (y * (bottom - top) / HEIGHT));
// Start off z at (0, 0).
complex<double> z(0.0, 0.0);
// Iterate z = z^2 + c until z moves more than 2 units
// away from (0, 0), or we've iterated too many times.
int iterations = 0;
while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
{
z = (z * z) + c;
++iterations;
}
/*if (iterations == MAX_ITERATIONS)
{
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
image[y][x] = 0x58DC77; // green
}*/
if (iterations <= 10)
{
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
image[y][x] = 0xA9C3F6; // light blue
}
else if (iterations <=100)
{
// This point is in the Mandelbrot set.
image[y][x] = 0x36924B; // darkest green
}
else if (iterations <= 200)
{
// This point is in the Mandelbrot set.
image[y][x] = 0x5FB072; // lighter green
}
else if (iterations <= 300)
{
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
image[y][x] = 0x7CD891; // mint green
}
else if (iterations <= 450)
{
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
image[y][x] = 0x57F97D; // green
}
else
{
// z escaped within less than MAX_ITERATIONS
// iterations. This point isn't in the set.
image[y][x] = 0x58DC77; // light green
}
}
}
}
int main(int argc, char *argv[])
{
cout << "Processing" << endl;
// Start timing
the_clock::time_point start = the_clock::now();
// This shows the whole set.
compute_mandelbrot(-2.0, 1.0, 1.125, -1.125);
// This zooms in on an interesting bit of detail.
//compute_mandelbrot(-0.751085, -0.734975, 0.118378, 0.134488);
// Stop timing
the_clock::time_point end = the_clock::now();
// Compute the difference between the two times in milliseconds
auto time_taken = duration_cast<milliseconds>(end - start).count();
cout << "Computing the Mandelbrot set took " << time_taken << " ms." << endl;
write_tga("output.tga");
return 0;
}

Lets say you want to use N parallel threads for the rendering, then each thread will handle HEIGHT / N lines.
For simplicities sake I pick an N that divides your HEIGHT evenly, like 5. That means each thread will handle 20 lines each (with your HEIGHT being equal to 100).
You could implement it something like this:
constexpr int THREADS = 5; // Our "N", divides HEIGHT evenly
void compute_mandelbrot_piece(double left, double right, double top, double bottom, unsigned y_from, unsigned y_to)
{
for (unsigned y = y_from; y < y_to; ++y)
{
for (unsigned x = 0; y < WIDTH; ++x)
{
// Existing code to calculate value for y,x
// ...
}
}
}
void compute_mandelbrot(double left, double right, double top, double bottom)
{
std::vector<std::thread> render_threads;
render_threads.reserve(THREADS); // Allocate memory for all threads, keep the size zero
// Create threads, each handling part of the image
for (unsigned y = 0; y < HEIGHT; y += HEIGHT / THREADS)
{
render_threads.emplace_back(&compute_mandelbrot_piece, left, right, top, bottom, y, y + HEIGHT / THREADS);
}
// Wait for the threads to finish, and join them
for (auto& thread : render_threads)
{
thread.join();
}
// Now all threads are done, and the image should be fully rendered and ready to save
}

Related

Progress Bar Causes Program to Halt and Lock, How Can I Fix It?

Below is the current code that I am working with. When I comment out the code to run the progress_bar function, code works perfectly as expected with the mandelbrot printed out into a seperate image file. Yet for whatever reason, when I try to include the function it screeches everything to a halt when the rest of the program has finished up. How do I include the progress_bar function without the program locking into a pseudo-deadlock state? Any and all help is appreciated.
#include <cstdint>
#include <cstdlib>
#include <complex>
#include <fstream>
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
// Import things we need from the standard library
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::complex;
using std::cout;
using std::endl;
using std::ofstream;
// Define the alias "the_clock" for the clock type we're going to use.
typedef std::chrono::steady_clock the_clock;
// The size of the image to generate.
const int WIDTH = 1920;
const int HEIGHT = 1200;
// The number of times to iterate before we assume that a point isn't in the
// Mandelbrot set.
// (You may need to turn this up if you zoom further into the set.)
const int MAX_ITERATIONS = 500;
// The image data.
// Each pixel is represented as 0xRRGGBB.
uint32_t image[HEIGHT][WIDTH];
double progress;
bool progressDone = false;
std::mutex locking;
std::condition_variable conditionMet;
int partsDone = 0;
// Write the image to a TGA file with the given name.
// Format specification: http://www.gamers.org/dEngine/quake3/TGA.txt
void progress_bar() {
std::unique_lock<std::mutex> lock(locking);
while (!progressDone) {
//std::this_thread::sleep_for(std::chrono::nanoseconds(100));
cout << "Current Progress is at: " << progress << "%\n";
conditionMet.wait(lock);
}
cout << "Mandelbrot is finished! Take a look.";
}
void write_tga(const char *filename)
{
ofstream outfile(filename, ofstream::binary);
uint8_t header[18] = {
0, // no image ID
0, // no colour map
2, // uncompressed 24-bit image
0, 0, 0, 0, 0, // empty colour map specification
0, 0, // X origin
0, 0, // Y origin
WIDTH & 0xFF, (WIDTH >> 8) & 0xFF, // width
HEIGHT & 0xFF, (HEIGHT >> 8) & 0xFF, // height
24, // bits per pixel
0, // image descriptor
};
outfile.write((const char *)header, 18);
for (int y = 0; y < HEIGHT; ++y)
{
for (int x = 0; x < WIDTH; ++x)
{
uint8_t pixel[3] = {
image[y][x] & 0xFF, // blue channel
(image[y][x] >> 8) & 0xFF, // green channel
(image[y][x] >> 16) & 0xFF, // red channel
};
outfile.write((const char *)pixel, 3);
}
}
outfile.close();
if (!outfile)
{
// An error has occurred at some point since we opened the file.
cout << "Error writing to " << filename << endl;
exit(1);
}
}
// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot(double left, double right, double top, double bottom, double start, double finish)
{
for (int y = start; y < finish; ++y)
{
for (int x = 0; x < WIDTH; ++x)
{
// Work out the point in the complex plane that
// corresponds to this pixel in the output image.
complex<double> c(left + (x * (right - left) / WIDTH),
top + (y * (bottom - top) / HEIGHT));
// Start off z at (0, 0).
complex<double> z(0.0, 0.0);
// Iterate z = z^2 + c until z moves more than 2 units
// away from (0, 0), or we've iterated too many times.
int iterations = 0;
while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
{
z = (z * z) + c;
++iterations;
}
if (iterations == MAX_ITERATIONS)
{
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
image[y][x] = 0x000000; // black
}
else if (iterations == 0) {
image[y][x] = 0xFFFFFF;
}
else
{
// z escaped within less than MAX_ITERATIONS
// iterations. This point isn't in the set.
image[y][x] = 0xFFFFFF; // white
image[y][x] = 16711680 | iterations << 8 | iterations;
}
std::unique_lock<std::mutex> lock(locking);
progress += double((1.0 / (WIDTH*HEIGHT)) * 100.0);
conditionMet.notify_one();
}
}
partsDone += 1;
}
int main(int argc, char *argv[])
{
cout << "Please wait..." << endl;
// Start timing
std::vector<std::thread*> threads;
the_clock::time_point start = the_clock::now();
std::thread progressive(progress_bar);
for (int slice = 0; slice < 2; slice++) {
// This shows the whole set.
threads.push_back(new std::thread(compute_mandelbrot, -2.0, 1.0, 1.125, -1.125, HEIGHT * (slice / 2), HEIGHT * ((slice + 1) / 2)));
// This zooms in on an interesting bit of detail.
//compute_mandelbrot(-0.751085, -0.734975, 0.118378, 0.134488, 0, HEIGHT/16);
}
// Stop timing
for (std::thread* t : threads) {
t->join();
delete t;
}
if (partsDone == 2) {
progressDone = true;
}
progressive.join();
the_clock::time_point end = the_clock::now();
// Compute the difference between the two times in milliseconds
auto time_taken = duration_cast<milliseconds>(end - start).count();
cout << "Computing the Mandelbrot set took " << time_taken << " ms." << endl;
write_tga("output.tga");
std::this_thread::sleep_for(milliseconds(3000));
return 0;
}```
The reason for your non-termination is that no-one notifies the progress bar thread after all the work has finished. Add conditionMet.notify_one(); before the call to progressive.join(). I've ommitted IO to be able to run in an online compiler in the following Demo. Also (as #GoswinvonBrederlow mentions in the comments) make sure to turn partsDone into std::atomic because if >1 threads call partsDone += 1 you'll end up with undefined results and in turn you won't be able to tell if you program is finished.
This would all look simpler if you changed progress to std::atomic and had your progress printer just load the variable in say 100ms intervals (and printed on top of the previous line). Then all you'd need would be the progressDone flag instead of locking and printing for every modification of the progress value. You can see in the following Demo that this runs with zero thread sanitizer warnings. Make sure to adjust the printing interval. This change drops the runtime from ~10.7s to 7s, even though that is just an indication - it's not kosher to time your programs with the thread sanitizer on.

pragma omp parallel for does not speed up my program

I have tried adding #pragma parallel omp for to this raytracing program and am measuring the same/very similar amount of processing time with and without the pragma statement.
This is the function:
void Scene::SaveImage(
const char *outPngFileName,
int pixelsWide,
int pixelsHigh,
double zoom,
int antiAliasFactor) const
{
// Oversample the image using the anti-aliasing factor.
const int largePixelsWide = antiAliasFactor * pixelsWide;
const int largePixelsHigh = antiAliasFactor * pixelsHigh;
const int smallerDim =
((pixelsWide < pixelsHigh) ? pixelsWide : pixelsHigh);
const double largeZoom = antiAliasFactor * zoom * smallerDim;
ImageBuffer buffer(largePixelsWide, largePixelsHigh, backgroundColor);
// The camera is located at the origin.
Vector camera(0.0, 0.0, 0.0);
// The camera faces in the -z direction.
// This allows the +x direction to be to the right,
// and the +y direction to be upward.
Vector direction(0.0, 0.0, -1.0);
const Color fullIntensity(1.0, 1.0, 1.0);
// We keep a list of (i,j) screen coordinates for pixels
// we are not able to trace definitive rays for.
// Later we will come back and fix these pixels.
PixelList ambiguousPixelList;
#pragma omp parallel for
for (int i=0; i < largePixelsWide; ++i)
{
direction.x = (i - largePixelsWide/2.0) / largeZoom;
for (int j = 0; j < largePixelsHigh; ++j)
{
direction.y = (largePixelsHigh / 2.0 - j) / largeZoom;
#if RAYTRACE_DEBUG_POINTS
{
using namespace std;
// Assume no active debug point unless we find one below.
activeDebugPoint = NULL;
DebugPointList::const_iterator iter = debugPointList.begin();
DebugPointList::const_iterator end = debugPointList.end();
for(; iter != end; ++iter)
{
if ((iter->iPixel == i) && (iter->jPixel == j))
{
cout << endl;
cout << "Hit breakpoint at (";
cout << i << ", " << j <<")" << endl;
activeDebugPoint = &(*iter);
break;
}
}
}
#endif
PixelData& pixel = buffer.Pixel(i,j);
try
{
// Trace a ray from the camera toward the given direction
// to figure out what color to assign to this pixel.
pixel.color = TraceRay(
camera,
direction,
ambientRefraction,
fullIntensity,
0);
}
catch (AmbiguousIntersectionException)
{
// Getting here means that somewhere in the recursive
// code for tracing rays, there were multiple
// intersections that had minimum distance from a
// vantage point. This can be really bad,
// for example causing a ray of light to reflect
// inward into a solid.
// Mark the pixel as ambiguous, so that any other
// ambiguous pixels nearby know not to use it.
pixel.isAmbiguous = true;
// Keep a list of all ambiguous pixel coordinates
// so that we can rapidly enumerate through them
// in the disambiguation pass.
ambiguousPixelList.push_back(PixelCoordinates(i, j));
}
}
}
#if RAYTRACE_DEBUG_POINTS
// Leave no chance of a dangling pointer into debug points.
activeDebugPoint = NULL;
#endif
// Go back and "heal" ambiguous pixels as best we can.
PixelList::const_iterator iter = ambiguousPixelList.begin();
PixelList::const_iterator end = ambiguousPixelList.end();
for (; iter != end; ++iter)
{
const PixelCoordinates& p = *iter;
ResolveAmbiguousPixel(buffer, p.i, p.j);
}
// We want to scale the arbitrary range of
// color component values to the range 0..255
// allowed by PNG format. We therefore find
// the maximum red, green, or blue value anywhere
// in the image.
const double max = buffer.MaxColorValue();
// Downsample the image buffer to an integer array of RGBA
// values that LodePNG understands.
const unsigned char OPAQUE_ALPHA_VALUE = 255;
const unsigned BYTES_PER_PIXEL = 4;
// The number of bytes in buffer to be passed to LodePNG.
const unsigned RGBA_BUFFER_SIZE =
pixelsWide * pixelsHigh * BYTES_PER_PIXEL;
std::vector<unsigned char> rgbaBuffer(RGBA_BUFFER_SIZE);
unsigned rgbaIndex = 0;
const double patchSize = antiAliasFactor * antiAliasFactor;
//#pragma parallel for collapse(3)
for (int j=0; j < pixelsHigh; ++j)
{
for (int i=0; i < pixelsWide; ++i)
{
Color sum(0.0, 0.0, 0.0);
for (int di=0; di < antiAliasFactor; ++di)
{
//#pragma parallel omp for reduction(+:sum)
for (int dj=0; dj < antiAliasFactor; ++dj)
{
sum += buffer.Pixel(
antiAliasFactor*i + di,
antiAliasFactor*j + dj).color;
}
}
sum /= patchSize;
// Convert to integer red, green, blue, alpha values,
// all of which must be in the range 0..255.
rgbaBuffer[rgbaIndex++] = ConvertPixelValue(sum.red, max);
rgbaBuffer[rgbaIndex++] = ConvertPixelValue(sum.green, max);
rgbaBuffer[rgbaIndex++] = ConvertPixelValue(sum.blue, max);
rgbaBuffer[rgbaIndex++] = OPAQUE_ALPHA_VALUE;
}
}
// Write the PNG file
const unsigned error = lodepng::encode(
outPngFileName,
rgbaBuffer,
pixelsWide,
pixelsHigh);
// If there was an encoding error, throw an exception.
if (error != 0)
{
std::string message = "PNG encoder error: ";
message += lodepng_error_text(error);
throw ImagerException(message.c_str());
}
}
// The following function searches through all solid objects
// for the first solid (if any) that contains the given point.
// In the case of ties, the solid that was inserted into the
// scene first wins. This arbitrary convention allows the
// composer of a scene to decide which of multiple overlapping
// objects should control the index of refraction for any
// overlapping volumes of space.
const SolidObject* Scene::PrimaryContainer(const Vector& point) const
{
SolidObjectList::const_iterator iter = solidObjectList.begin();
SolidObjectList::const_iterator end = solidObjectList.end();
for (; iter != end; ++iter)
{
const SolidObject* solid = *iter;
if (solid->Contains(point))
{
return solid;
}
}
return NULL;
}
This is the snippet I added it to:
#pragma omp parallel for
for (int i=0; i < largePixelsWide; ++i)
{
direction.x = (i - largePixelsWide/2.0) / largeZoom;
for (int j = 0; j < largePixelsHigh; ++j)
{
direction.y = (largePixelsHigh / 2.0 - j) / largeZoom;
I have made sure all of my VS17 settings are correct and -fopenmp main.cpp is on command arguments. Additional information: This is in another file called scene.cpp, omp header file is added to scene.cpp.
My questions are how can I get this to work and if there are other parts where I can add it where/how would I go about doing it. Thank you.

What is wrong with my algorithm to render rectangle in 1D array?

I am trying to make optimal algorithm to draw rectangle onto 1D array. I wrote this function:
/** Draws a rectangle in 1D array
* Arguments:
* pixmap - 1D array of Color
* color - rectangle color
* w - rectangle width
* h - rectanhle height
* x - x position, negative coordinates are outside draw area
* y - y position, negative coordinates are outside draw area
* pixmapWidth - width of the image (height can be deducted from width if needed but is practically unnecessary) */
void rectangle(std::vector<int>& pixmap, const int& color, const int w, const int h, int x, const int y, const int pixmapWidth)
{
if(x>=pixmapWidth)
return;
if(x+w<0)
return;
if(y+h<0)
return;
// Width of one consistent line of color of the rectangle
// if the rectangle is partially out of pixmap area,
// thw width is smaller than rectangle width
const int renderWidth = std::min(w, pixmapWidth-x);
// offset in the arrray where the rendering starts
// 0 would be for [0,0] coordinate
int tg_offset = y*pixmapWidth+x;
// maximum offset to ever render, which is the array size
const int tg_end = pixmap.size();
int lines = 0;
for(; tg_offset<tg_end && lines<h; tg_offset+=pixmapWidth) {
for(int cx=0; cx<renderWidth; ++cx) {
// This check keeps failing and my program crashes
if(tg_offset+cx >= pixmap.size())
throw "Oh no, what a bad thing to happen!";
pixmap[tg_offset+cx] = color;
}
lines++;
}
}
Note that I know there's a lot of picture drawing libraries, but I'm trying to learn by doing this. But now I'm stuck and I need help.
The problem is that in the inner loop, condition if(tg_offset+cx >= pixmap.size()) keeps failing meaning I am trying to render outside the array. I have no idea why this keeps happening.
Example problematic code:
const int pixmap_width = 20;
const int pixmap_height = 20;
std::vector<int> pixmap(pixmap_width*pixmap_height);
// tries to render outside the array
rectangle(pixmap, 0, 10, 10, -1, 18, pixmap_width);
Here is a testcase including ASCII output of the pixmap: http://ideone.com/SoJPFF
I don't know how could I improve the question any more...
Making no changes produces a quadrilateral. Is this not the desired functionality?
for(; tg_offset<tg_end && lines<h; tg_offset+=pixmapWidth) {
cout <<"" << endl;
for(int cx=0; cx<renderWidth; ++cx) {
cout << " " << pixmap[tg_offset+cx];
// This check keeps failing and my program crashes
if(tg_offset+cx >= pixmap.size())
throw "Oh no, what a bad thing to happen!";
pixmap[tg_offset+cx] = color;
}
lines++;
}
}
int main()
{
std::vector<int> pixmap(16);
pixmap = { 1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1 };
int color = 0;
int w = 4;
int h = 4;
int x = 0;
int y = 0;
int pixmapWidth = 4;
cout << "Hello World" << endl;
rectangle(pixmap, color, w, h, x, y, pixmapWidth);
return 0;
}
produces:
Hello World
1 1 1 1
1 0 0 1
1 0 0 1
1 1 1 1
I think a large part of the problem with your function is it being a lot more complex than it needs to be. Here's a much simpler version of your function, done by simply looping over x and y.
void rectangle(std::vector<int>& pixmap, const int& color, const int width, const int height,
int left, const int top, const int pixmapWidth)
{
for (int x = std::max(left, 0); x < left + width && x < pixmapWidth; x++)
for (int y = std::max(top, 0); y < top + height && y*pixmapWidth + x < pixmap.size(); y++)
pixmap[y*pixmapWidth + x] = color;
}
I'm not sure exactly what the output you want when x or y are negative. In your actual algorithm things goes wrong if x is negative due the fact that tg_offset goes back, so the tg_offset + cx can fail.
To solve this you can limit the second for to avoid this, like this:
for(int cx=0; cx<std::min(renderWidth, tg_end - tg_offset); ++cx)
but I think that limiting x and y to be only positive is more correct:
if ( x < 0 ) x = 0;
if ( y < 0 ) y = 0;

Why is the image's Hog generation values so low in outdoor scene?

I am attempting to find Pedestriants/People in images with the help of a cascade classifier which uses HOG as features.
The problem I'm trying to solve is in the initial stage, feature generation.
Where the HOG values in certain areas of the images are too low and hence the classifier fails.
The images below were captured using a Basler aca640-100gc Camera.
The visualization of the HOG was borrowed from the code in the webpage. Code also attached in the end of the question.
This first image here and its HOG is what I'm trying to achieve.
A realistic outdoor scene which can be used to generate features and hopefully find people. This is not what I have captured using my camera.
Captured Outdoor Images results
The images below are what I have created with the camera. I have tried all basic variations where I have played with the brightness and Focus But this still yeilds a poor result in an outdoor scene. Where I am inside the car and the camera is attached close to the windscreen.
But on the Contrary when the same camera was used to record indoor scene It works fine. Why it works when its in an indoor situtation and why not in an outdoor scene is something I can't understand.
Captured Indoor Images results
As seen in the images below same configuration works for an indoor scene.
Desired results
Ideally I would like results of the out door recordings to look like so.
Could anyone give me insight why this happens?
or How I can over come this issue to generate reliable HOGs for detection?
Code to visualize HOG
Mat img_raw = imread("C:\\testimg.png", 1); // load as color image
resize(img_raw, img_raw, Size(64,128) );
Mat img;
cvtColor(img_raw, img, CV_RGB2GRAY);
HOGDescriptor d;
// Size(128,64), //winSize
// Size(16,16), //blocksize
// Size(8,8), //blockStride,
// Size(8,8), //cellSize,
// 9, //nbins,
// 0, //derivAper,
// -1, //winSigma,
// 0, //histogramNormType,
// 0.2, //L2HysThresh,
// 0 //gammal correction,
// //nlevels=64
//);
// void HOGDescriptor::compute(const Mat& img, vector<float>& descriptors,
// Size winStride, Size padding,
// const vector<Point>& locations) const
vector<float> descriptorsValues;
vector<Point> locations;
d.compute( img, descriptorsValues, Size(8,8), Size(8,8), locations);
cout << "HOG descriptor size is " << d.getDescriptorSize() << endl;
cout << "img dimensions: " << img.cols << " width x " << img.rows << "height" << endl;
cout << "Found " << descriptorsValues.size() << " descriptor values" << endl;
cout << "Nr of locations specified : " << locations.size() << endl;
Mat get_hogdescriptor_visual_image(Mat& origImg,
vector<float>& descriptorValues,
Size winSize,
Size cellSize,
int scaleFactor,
double viz_factor)
{
Mat visual_image;
resize(origImg, visual_image, Size(origImg.cols*scaleFactor, origImg.rows*scaleFactor));
int gradientBinSize = 9;
// dividing 180° into 9 bins, how large (in rad) is one bin?
float radRangeForOneBin = 3.14/(float)gradientBinSize;
// prepare data structure: 9 orientation / gradient strenghts for each cell
int cells_in_x_dir = winSize.width / cellSize.width;
int cells_in_y_dir = winSize.height / cellSize.height;
int totalnrofcells = cells_in_x_dir * cells_in_y_dir;
float*** gradientStrengths = new float**[cells_in_y_dir];
int** cellUpdateCounter = new int*[cells_in_y_dir];
for (int y=0; y<cells_in_y_dir; y++)
{
gradientStrengths[y] = new float*[cells_in_x_dir];
cellUpdateCounter[y] = new int[cells_in_x_dir];
for (int x=0; x<cells_in_x_dir; x++)
{
gradientStrengths[y][x] = new float[gradientBinSize];
cellUpdateCounter[y][x] = 0;
for (int bin=0; bin<gradientBinSize; bin++)
gradientStrengths[y][x][bin] = 0.0;
}
}
// nr of blocks = nr of cells - 1
// since there is a new block on each cell (overlapping blocks!) but the last one
int blocks_in_x_dir = cells_in_x_dir - 1;
int blocks_in_y_dir = cells_in_y_dir - 1;
// compute gradient strengths per cell
int descriptorDataIdx = 0;
int cellx = 0;
int celly = 0;
for (int blockx=0; blockx<blocks_in_x_dir; blockx++)
{
for (int blocky=0; blocky<blocks_in_y_dir; blocky++)
{
// 4 cells per block ...
for (int cellNr=0; cellNr<4; cellNr++)
{
// compute corresponding cell nr
int cellx = blockx;
int celly = blocky;
if (cellNr==1) celly++;
if (cellNr==2) cellx++;
if (cellNr==3)
{
cellx++;
celly++;
}
for (int bin=0; bin<gradientBinSize; bin++)
{
float gradientStrength = descriptorValues[ descriptorDataIdx ];
descriptorDataIdx++;
gradientStrengths[celly][cellx][bin] += gradientStrength;
} // for (all bins)
// note: overlapping blocks lead to multiple updates of this sum!
// we therefore keep track how often a cell was updated,
// to compute average gradient strengths
cellUpdateCounter[celly][cellx]++;
} // for (all cells)
} // for (all block x pos)
} // for (all block y pos)
// compute average gradient strengths
for (int celly=0; celly<cells_in_y_dir; celly++)
{
for (int cellx=0; cellx<cells_in_x_dir; cellx++)
{
float NrUpdatesForThisCell = (float)cellUpdateCounter[celly][cellx];
// compute average gradient strenghts for each gradient bin direction
for (int bin=0; bin<gradientBinSize; bin++)
{
gradientStrengths[celly][cellx][bin] /= NrUpdatesForThisCell;
}
}
}
cout << "descriptorDataIdx = " << descriptorDataIdx << endl;
// draw cells
for (int celly=0; celly<cells_in_y_dir; celly++)
{
for (int cellx=0; cellx<cells_in_x_dir; cellx++)
{
int drawX = cellx * cellSize.width;
int drawY = celly * cellSize.height;
int mx = drawX + cellSize.width/2;
int my = drawY + cellSize.height/2;
rectangle(visual_image,
Point(drawX*scaleFactor,drawY*scaleFactor),
Point((drawX+cellSize.width)*scaleFactor,
(drawY+cellSize.height)*scaleFactor),
CV_RGB(100,100,100),
1);
// draw in each cell all 9 gradient strengths
for (int bin=0; bin<gradientBinSize; bin++)
{
float currentGradStrength = gradientStrengths[celly][cellx][bin];
// no line to draw?
if (currentGradStrength==0)
continue;
float currRad = bin * radRangeForOneBin + radRangeForOneBin/2;
float dirVecX = cos( currRad );
float dirVecY = sin( currRad );
float maxVecLen = cellSize.width/2;
float scale = viz_factor; // just a visual_imagealization scale,
// to see the lines better
// compute line coordinates
float x1 = mx - dirVecX * currentGradStrength * maxVecLen * scale;
float y1 = my - dirVecY * currentGradStrength * maxVecLen * scale;
float x2 = mx + dirVecX * currentGradStrength * maxVecLen * scale;
float y2 = my + dirVecY * currentGradStrength * maxVecLen * scale;
// draw gradient visual_imagealization
line(visual_image,
Point(x1*scaleFactor,y1*scaleFactor),
Point(x2*scaleFactor,y2*scaleFactor),
CV_RGB(0,0,255),
1);
} // for (all bins)
} // for (cellx)
} // for (celly)
// don't forget to free memory allocated by helper data structures!
for (int y=0; y<cells_in_y_dir; y++)
{
for (int x=0; x<cells_in_x_dir; x++)
{
delete[] gradientStrengths[y][x];
}
delete[] gradientStrengths[y];
delete[] cellUpdateCounter[y];
}
delete[] gradientStrengths;
delete[] cellUpdateCounter;
return visual_image;
}

How to add/subtract to value rather than just be that value

Using the Openframeworks library in C++, I have the radius of a glow (max_distance) that is determined by the stretch of the mouse dragging across the screen (mouseDragX). It works fine.
But rather than every time I resize it (by dragging the mouse), I want it not to start at 0 and follow the mouse drag directly.
max_distance = mouseDragX/2;
But rather, if I have already dragged the mouse to the right to say 200 on a previous drag, that the next time I drag the mouse, and go into the opposite direction (negative numbers) that the value of max_distance decreases by that amount, instead of just being that amount.
I thought it would be
max_distance += mouseDragX/2;
but that seems to kill it altogether
Can you help me?
#include "testApp.h"
//--------------------------------------------------------------
void testApp::setup(){
ofSetWindowShape(700,700);
max_distance = 700; // ofDist didn't work(?) // ofDist(0,0,700,700);
ofEnableSmoothing();
ofEnableAlphaBlending();
}
//--------------------------------------------------------------
void testApp::update(){
max_distance = mouseDragX/2;
if (max_distance < 0) max_distance = 0;
}
//--------------------------------------------------------------
void testApp::draw(){
string str = "mouseDragX: ";
str += ofToString(mouseDragX)+" ";
ofSetWindowTitle(str);
int i,j;
int height = ofGetHeight();
int width = ofGetWidth();
for(i = 0; i <= height; i += 20) {
for(j = 0; j <= width; j += 20) {
float dist_color = getDistance(mouseX, mouseY, i, j); // for definition of getDistance, look below!
dist_color = dist_color/max_distance * 100;
// to get the colors into the range between 0 and 255, multiply the values by 5.
ofSetColor(dist_color*5,dist_color*5,dist_color*5, 123);
ofEllipse(i, j, 20, 20);
}
}
}
//--------------------------------------------------------------
void testApp::keyPressed (int key){
}
//--------------------------------------------------------------
void testApp::keyReleased (int key){
}
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
// shift values down
for (int i = 0; i < 1; /*<<- length of array*/ i++) {
pmouseX[i] = pmouseX[i+1];
pmouseY[i] = pmouseY[i+1];
}
// make pmouseX/Y[0] be the previous mouse position. [1] = current
pmouseX[1] = mouseX;
pmouseY[1] = mouseY;
}
//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
mouseDragX = (mouseX - pmouseX[0]);
}
//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
// mouseDragX = mouseDragY = 0; // The drag starts here
}
//--------------------------------------------------------------
void testApp::mouseReleased(){
}
float testApp::getDistance(int startX, int startY, int endX, int endY){
return sqrt((endX-startX)*(endX-startX) + (endY-startY)*(endY-startY));
}
Thank you so much.
If I understand correctly, you want todo something like this.
// Every time the mouse *stops* moving, (say on mouse-up
// message) save previous max_distance
int base = max_distance;
// when mouse moves
max_distance = base + mouseDragX/2;
If max_distance and mouseDragX are int values, the division by 2 results in an integer division that can induce losses.
This is especially true if mouseDragX value's is 1 at some time. This will result in 1 / 2 (integer division) and returns 0.
Example:
Lets consider that mouseDragX takes 3 different values (3 cycles):
3, 1, -4
One would expect that max_distance will be increased by (3 / 2) + (1 / 2) - (4 / 2) = 0.
But due to integer truncation, this will infact result to 1 + 0 - 2 = -1.
What if you use floats instead of int, and just round max_distance to an int when you really need it's value ?