vector<double> SimulatedAnnealing::FindOptimalParameters(const vector<double>& initial_parameters) { vector<double> current_parameters = initial_parameters; for (size_t i = 1; i <= max_number_of_iterations_; ++i) { vector<double> neighbour = GetRandomNeighbour(current_parameters); double current_value = CountObjectiveFunctionValueToMinimize(current_parameters); double neighbour_value = CountObjectiveFunctionValueToMinimize(neighbour); double t = CountTemperature(i); double probability = CountAcceptanceProbability(current_value, neighbour_value, t); if (Random::Instance().CheckEventOutcome(probability)) { current_parameters = neighbour; } } return current_parameters; }
void EdgeLayer(QImage* img, QImage* canvas, int radius, double hue_distortion, double strength) /// /// This final layer repaints over areas determined to be edges in order to bring smaller details /// that have been covered by points back into the picture. The same color distortions are used /// as in the main layer. /// /// @param img /// The reference image. /// /// @param canvas /// The canvas to be painted to. /// /// @param radius /// The radius of the points to be painted. /// /// @param hue_distortion /// Determines the strength of the hue distortion where 1.0 is very distorted and 0.0 is not distorted. /// /// @param strength /// The strength of the pointillistic filter, where 1.0 is very strong and 0.0 is very weak. /// /// @return /// Nothing. /// { hue_distortion = hue_distortion*strength; if( strength < 0.5 ) { int new_radius = (int)radius*strength*2; if( 1.0*new_radius < radius*strength*2 ) new_radius++; radius = new_radius; if( radius < 1 ) radius = 1; } uchar* edges = new uchar[img->width()*img->height()]; ImageProcessing::CannyEdgeDetection( img->bits(), edges, img->width(), img->height(), 4 ); uchar* gray = new uchar[img->width()*img->height()]; ImageProcessing::ConvertToOneChannel( img->bits(), gray, img->width(), img->height()); uchar* smoothed_gray = new uchar[img->width()*img->height()]; int kernel = radius; if(kernel%2 == 0) kernel++; if(kernel < 3) kernel = 3; ImageProcessing::GaussianBlur( gray, smoothed_gray, img->width(), img->height(), 1, kernel ); delete [] gray; // Clear the depth buffer ready for painting uchar* depth_buffer = new uchar[img->width()*img->height()]; for( int i = 0; i < img->width()*img->height(); i++ ) { depth_buffer[i] = 0; } // If there is an edge, find the greatest error in the edge's neighbourhood // and at a new stroke at this point. for( int y = 0; y < img->height(); y++ ) { for( int x = 0; x < img->width(); x++ ) { if( edges[y*img->width() + x] > 0 ) { // Find the brightest and and darkest spots in the neighbourhood int brightest = 0; int darkest = 255; QPoint brightest_pos = QPoint(0, 0); QPoint darkest_pos = QPoint(0, 0); for( int j = y - radius; j <= y + radius; j++ ) { for( int i = x - radius; i <= x + radius; i++ ) { if( i >= 0 && j >= 0 && i < img->width() && j < img->height() ) { int intensity = smoothed_gray[j*img->width() + i]; if(intensity > brightest) { brightest_pos = QPoint(i, j); brightest = intensity; } if(intensity < darkest) { darkest_pos = QPoint(i, j); darkest = intensity; } } } } // For each position in the neighbourhood, find if most spots // are closer to the brightest or darkest int dark = 0; int bright = 0; for( int j = y - radius; j <= y + radius; j++ ) { for( int i = x - radius; i <= x + radius; i++ ) { if( i >= 0 && j >= 0 && i < img->width() && j < img->height() ) { int intensity = smoothed_gray[j*img->width() + i]; int bright_diff = brightest - intensity; int dark_diff = intensity - darkest; if(bright_diff < dark_diff) { bright++; } else { dark++; } } } } // Paint at the side that needs defining QPoint new_point; if(bright < dark && bright != 0) { new_point = brightest_pos; } else if(dark != 0) { new_point = darkest_pos; } else { new_point = QPoint(x, y); } // Paint circle at this position QColor hsv = QColor(img->pixel( new_point )).toHsv(); int hue = hsv.hue(); int val = hsv.value(); int sat = hsv.saturation(); // Find closest hue in palette int new_pos = GetPaletteHuePosition( hsv.hue() ); if( ( rand()%100)/100.0 < strength ) { hue = GetRandomNeighbour( new_pos ); } else { hue = chevreul[new_pos]; } // Hue distortion double r = ( rand()%100 )/100.0; if( ( r < hue_distortion && new_pos != 8 ) || r < hue_distortion/3 ) { int n = ChangeHue(smoothed_gray[new_point.y()*img->width() + new_point.x()]/256.0)*2; if( n > -1 ) { new_pos = n; if( sat < 70 && val < 0.3 ) sat = 70; } hue = chevreul[new_pos]; } sat = ChangeSaturation( sat, val, 0.35*strength, strength ); int z = rand()%256; hsv.setHsv( hue, sat, val ); DrawRandomCircle( canvas, new_point, hsv.toRgb(), radius - 1, z, depth_buffer ); } } } delete [] smoothed_gray; delete [] edges; delete [] depth_buffer; }
void MainLayer( QImage* img, QImage* canvas, int radius, double strength ) /// /// Paint the main pointillism layer, adding smaller details and more color distortion. /// Points are painted where the color error between the canvas and the original image /// is high. Color difference is based on color intensity (i.e. brightness) to avoid /// difference due to hue distortion. Saturation distortion and divisionism are applied /// in addition to palette restriction. /// /// @param img /// The reference image. /// /// @param canvas /// The canvas to be painted to. /// /// @param radius /// The radius of the points being painted. /// /// @param strength /// The strength of the pointillistic filter, where 1.0 is very strong and 0.0 is very weak. /// /// @return /// Nothing. /// { // Adjust the point radius based on the strength of the filter if(strength < 0.5) { int new_radius = (int)radius*strength*2; if(1.0*new_radius < radius*strength*2) new_radius++; radius = new_radius; if(radius < 1) radius = 1; } // Get gray scale of original image uchar* gray = new uchar[img->width()*img->height()]; ImageProcessing::ConvertToOneChannel( img->bits(), gray, img->width(), img->height() ); // Blur the grayscale image uchar* smoothed_gray = new uchar[img->width()*img->height()]; int kernel = radius; if(kernel%2 == 0) kernel++; if(kernel < 3) kernel = 3; ImageProcessing::GaussianBlur( gray, smoothed_gray, img->width(), img->height(), 1, kernel ); delete [] gray; // Clear the depth buffer ready for painting uchar* depth_buffer = new uchar[img->width()*img->height()]; for( int i = 0; i < img->width()*img->height(); i++ ) { depth_buffer[i] = 0; } // At each grid point, find maximum error based on difference // between intensity at canvas and intensity of blurred image // Paint stroke at this location for( int y = (int)radius/2; y < img->height(); y += radius ) { for( int x = (int)radius/2; x < img->width(); x += radius ) { int total_error = 0; int max_error = 0; QPoint max_error_at = QPoint(0, 0); // Get error of each pixel in the neighbourhood int min_x = x - radius/2; int min_y = y - radius/2; int max_x = x + radius/2; int max_y = y + radius/2; if(min_x < 0) min_x = 0; if(min_y < 0) min_y = 0; if(max_x >= img->width()) max_x = img->width() - 1; if(max_y >= img->height()) max_y = img->height() - 1; for( int j = min_y; j <= max_y; j++ ) { for( int i = min_x; i <= max_x; i++ ) { // Get error at this pixel int intensity = QColor(canvas->pixel( i, j )).toHsv().value(); int error = abs(intensity - smoothed_gray[j*img->width() + i]); // Update error stats total_error += error; if( error > max_error ) { max_error = error; max_error_at = QPoint( i, j ); } } } // If the total error is above a threshold // Paint a stroke at the area of max error if( total_error > 10*strength ) { QColor hsv = QColor(img->pixel( x, y )).toHsv(); int hue = hsv.hue(); int sat = hsv.saturation(); int v = hsv.value(); // Find closest hue in palette, make a method that does this int new_pos = GetPaletteHuePosition( hsv.hue() ); if( (rand()%100)/100.0 < strength ) { hue = GetRandomNeighbour(new_pos); } else { hue = chevreul[new_pos]; } sat = ChangeSaturation(sat, v, 0.35*strength, strength); hsv.setHsv( hue, sat, v ); int z = rand()%256; DrawRandomCircle(canvas, max_error_at, hsv.toRgb(), radius, z, depth_buffer); } } } delete [] smoothed_gray; delete [] depth_buffer; }