I'm making an image editing program in c++ using sfml and tried to add image filters using:
int clamp(int value, int min, int max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}
void MyImage::applyKernel(std::vector<std::vector<int>> kernel)
{
int index(0), tempx(0), tempy(0);
int wr(0), wg(0), wb(0), wa(0), sum(0);
auto newPixels = new sf::Uint8[this->size_y * this->size_x * 4];
// Calculate the sum of the kernel
for (int i = 0; i < kernel.size(); i++) {
for (int j = 0; j < kernel[i].size(); j++) {
sum += kernel[i][j];
}
}
for (int y = 0; y < this->size_y; y++) {
for (int x = 0; x < this->size_x; x++) {
/*
Calculate weighted sum from kernel
*/
wr = wg = wb = wa = 0;
for (int i = 0; i < kernel.size(); i++) {
for (int j = 0; j < kernel[i].size(); j++) {
/*
Calculates the coordinates of the kernel relative to the pixel we are changing
*/
tempx = x + (j - floor(kernel[i].size() / 2));
tempy = y + (i - floor(kernel.size() / 2));
//std::cout << "kernel=(" << j << ", " << i << "), pixel=(" << x << ", " << y << ") tempPos=(" << tempx << ", " << tempy << ")\n";
/*
This code below should have the effect of mirroring the image in the case the kernel coordinate is out of bounds (along the edge of the image)
*/
tempx = (tempx < 0) ? -1 * tempx : tempx;
tempy = (tempy < 0) ? -1 * tempy : tempy;
tempx = (tempx > this->size_x) ? x - (j - floor(kernel[i].size() / 2)) : tempx;
tempy = (tempy > this->size_y) ? y - (i - floor(kernel.size() / 2)) : tempy;
if (tempx >= 0 && tempx < this->size_x && tempy >= 0 && tempy < this->size_y) {
index = (((tempy * this->size_x) - tempy) + (tempx)) * 4;
wr += kernel[i][j] * this->pixels[index];
wg += kernel[i][j] * this->pixels[index + 1];
wb += kernel[i][j] * this->pixels[index + 2];
wa += kernel[i][j] * this->pixels[index + 3];
}
}
}
if (sum) {
wr /= sum;
wg /= sum;
wb /= sum;
wa /= sum;
}
index = (((y * this->size_x) - y) + (x)) * 4;
newPixels[index] = clamp(wr, 0, 255); // Red
newPixels[index + 1] = clamp(wg, 0, 255); // Green
newPixels[index + 2] = clamp(wb, 0, 255); // Blue
newPixels[index + 3] = clamp(wa, 0, 255); // Alpha
}
}
this->pixels = newPixels;
// Copies the data from our sf::Uint8 array to the image object to be displayed => Removes the overhead of calling setPixel(x,y,color) for every pixel {As a side note setPixel() should always be avoided}|
this->im->create(this->size_x, this->size_y, this->pixels);
}
I was trying to use [-1,-1,-1], [-1,8,-1]. [-1,-1,-1] for edge detection but just ended up with a white image except for some pixels near the bottom. I've tried different images and kernels out but any that add to 0 don't work. For example if I take the edge detection kernel above and change the 8 to a 9, it gives an expected result. Is there something wrong with my idea of how convolution kernels work or is it just a bug in my code?
Thank you.
Related
I am working on creating my own implementation of a separable sobel filter implementation. My function has as input the kernelSize, the horizontal filter of gradient Y as pixelsY1, the vertical filter of gradient Y as pixelsY2, the horizontal filter of gradient X as pixelsX1, the vertical filter of gradient X as pixelsX2.
The input of X1 is [1, 0, -1] (horizontal)
The input of X2 is [1, 2, 1] (vertical)
The input of Y1 is [1, 2, 1] (horizontal)
The input of Y2 is [1, 0 -1] (vertical)
void gradientFilter1D(Mat& img, int kernelSize, vector<double> pixelsY1, vector<double> pixelsY2, vector<double> pixelsX1, vector<double> pixelsX2)
{
int sumMin = INT_MAX, sumMax = INT_MIN;
//gradient X
vector<vector<int>> pixelsX(img.rows, vector<int>(img.cols, 0));
//gradient Y
vector<vector<int>> pixelsY(img.rows, vector<int>(img.cols, 0));
vector<vector<int>> sumArray(img.rows, vector<int>(img.cols, 0));
for (int j = kernelSize / 2; j < img.rows - kernelSize / 2; j++)
{
for (int i = kernelSize / 2; i < img.cols - kernelSize / 2; i++)
{
double totalX = 0;
double totalY = 0;
//this is the horizontal multiplication
for (int x = -kernelSize / 2; x <= kernelSize / 2; x++)
{
totalY += img.at<uchar>(j, i + x) * pixelsY1[x + (kernelSize / 2)];
totalX += img.at<uchar>(j, i + x) * pixelsX1[x + (kernelSize / 2)];
//cout << int(img.at<uchar>(j, i + x)) << " " << pixelsY1[x + (kernelSize / 2)] << endl;
}
pixelsX[j][i] = totalX;
pixelsY[j][i] = totalY;
}
}
for (int j = kernelSize / 2; j < img.rows - kernelSize / 2; j++)
{
for (int i = kernelSize / 2; i < img.cols - kernelSize / 2; i++)
{
double totalX = 0;
double totalY = 0;
//this is the vertical multiplication
for (int x = -kernelSize / 2; x <= kernelSize / 2; x++)
{
totalY += pixelsY[j + x][i] * pixelsY2[x + (kernelSize / 2)];
totalX += pixelsX[j + x][i] * pixelsX2[x + (kernelSize / 2)];
//cout << int(img.at<uchar>(j, i + x)) << " " << pixelsY1[x + (kernelSize / 2)] << endl;
}
pixelsX[j][i] = totalX;
pixelsY[j][i] = totalY;
}
}
for (int j = 0; j < img.rows; j++)
{
for (int i = 0; i < img.cols; i++)
{
int sum;
sum = sqrt(pow(pixelsX[j][i], 2) + pow(pixelsY[j][i], 2));
sumArray[j][i] = sum;
sumMin = sumMin < sum ? sumMin : sum;
sumMax = sumMax > sum ? sumMax : sum;
}
}
//normalization
for (int j = 0; j < img.rows; j++)
for (int i = 0; i < img.cols; i++)
{
sumArray[j][i] = (sumArray[j][i] - sumMin) * ((255.0 - 0) / (sumMax - sumMin)) + 0;
img.at<uchar>(j, i) = sumArray[j][i];
}
}
Input Image:
Output Image:
What am I doing wrong?
The separable filter is computed in what are effectively two passes. (The passes can be interleaved, but all the values used by the vertical filter have to have already been computed by the horizontal filter if doing it in that order.) Right below the comment //then here I do the vertical multiplication there are accesses to pixelsX and pixelsY that are effectively a second pass of the separable filter. The values being accessed for negative values of x have been previously computed and the ones for positive values of x have not yet been computed by the horizontal pass.
check out Halide. It makes this sort of code a lot easier and more performant. (A double nesting of std::vector is not a good way to go.)
Okay, so my mistake was actually in this
for (int j = kernelSize / 2; j < img.rows - kernelSize / 2; j++)
{
for (int i = kernelSize / 2; i < img.cols - kernelSize / 2; i++)
{
double totalX = 0;
double totalY = 0;
//this is the vertical multiplication
for (int x = -kernelSize / 2; x <= kernelSize / 2; x++)
{
totalY += pixelsY[j + x][i] * pixelsY2[x + (kernelSize / 2)];
totalX += pixelsX[j + x][i] * pixelsX2[x + (kernelSize / 2)];
//cout << int(img.at<uchar>(j, i + x)) << " " << pixelsY1[x + (kernelSize / 2)] << endl;
}
pixelsX[j][i] = totalX; <---- I overwrite the old values
pixelsY[j][i] = totalY; <--- I overwrite the old values
}
}
So, pixelsX[j][i] = totalX and so forth is wrong, because I need the old values in order to finish the computation in the rest of the j, and i loops. So, I created another vector of vectors and pushed in it the totalX's and Y's, and this solved my issue.
I have to implement a DCT algorithm in C++, here is my present code :
// dct: computes the discrete cosinus tranform of a 8x8 block
template<typename Tin=uchar,typename Tout=float>
inline cv::Mat_<Tout> dct(const cv::Mat_<Tin>& oBlock) {
int indexNumber;
float pi = 3.14159265359;
float fcoscos, fxy, cos1, cos2, forCos1, forCos2;
cv::Mat_<Tout> resultBloc(8, 8);
for (int u = 0; u < oBlock.rows; u++){
for (int v = 0; v < oBlock.cols; v++){
float cu=0, cv=0, Result=0;
// calcul c(u)
if (u == 0){
cu = (float)sqrt((float)1 / (float)oBlock.rows);
}
else {
cu = (float)sqrt((float)2 / (float)oBlock.rows);
}
// calcul c(v)
if (v == 0){
cv = (float)sqrt((float)1 / (float)oBlock.cols);
}
else {
cv = (float)sqrt((float)2 / (float)oBlock.cols);
}
float sums = 0;
for (int x = 0; x < oBlock.rows; x++){
for (int y = 0; y < oBlock.cols; y++){
indexNumber = x * oBlock.rows + y;
fxy = (int)oBlock.data[indexNumber];
forCos1 = (pi*((2 * x) + 1)*u) / (2 * oBlock.rows);
forCos2 = (pi*((2 * y) + 1)*v) / (2 * oBlock.cols);
cos1 = cos(forCos1);
cos2 = cos(forCos2);
fcoscos = fxy * cos1 * cos2;
sums += fcoscos;
}
}
// calcul total
Result = sums*cu*cv;
indexNumber = u * oBlock.rows + v;
resultBloc.data[indexNumber] = Result;
}
}
return resultBloc;
}
I compared the result with the cv DCT algorithm as follow :
cv::Mat_<float> tempImage(8,8);
for (int i = 0; i < vecImageCut[0].cols*vecImageCut[0].rows; i++){
tempImage.data[i] = (int)vecImageCut[0].data[i];
}
cv::Mat_<float> dctCV;
cv::dct(tempImage, dctCV);
for (int i = 0; i < blocksAfterDCT[0].cols*blocksAfterDCT[0].rows; i++){
std::cerr << "Difference DCT for pixel " << i << " : " << dctCV.data[i] - blocksAfterDCT[0].data[i] << std::endl;
}
The results between my DCT and the cv DCT are very different so i assume my DCT algorithm is wrong but i searched for hours and i can't find my mistake, can anyone tell me where i did something wrong ?
Your index calculations are wrong. In indexNumber = x * oBlock.rows + y;, since x is counting rows it needs to be multiplied by the number of columns:
indexNumber = x * oBlock.cols + y;
The same for indexNumber = u * oBlock.rows + v;
indexNumber = u * oBlock.cols + v;
I load the vertical and horizontal gradients into the function posted here and it calculates the sums which than make up the corner response. Why do only boarder pixels get to be found, my threshold is 0 otherwise there is 0 corners on the image. For gradients I used sobel operator.
Look at the output image below.
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if ((i - search_size / 2 < 0 || i + search_size / 2 > image1.rows - 1) || (j - search_size / 2 < 0 || j + search_size / 2 > image1.cols - 1)) {
continue;
}
double Ix2 = 0, Iy2 = 0, Ixy = 0;
double detM=0;
double traceM=0;
double R = 0;
for (int m = i-search_size /2; m < i + search_size /2 ; m++){
for (int n = j-search_size /2; n < j + search_size/2 ; n++){
gauss = exp(-(((i - m) * (i - m)) + ((j - n) * (j - n))) / gaus_del);
//Compute Ix^2 , Iy^2 and Ixy
Ix2 += gauss*(image1.at<float>(m, n)*image1.at<float>(m, n));
Iy2 += gauss*(image2.at<float>(m, n)*image2.at<float>(m, n));
Ixy += gauss*(image1.at<float>(m, n)*image2.at<float>(m, n));
}
}
detM = (Ix2*Iy2 - Ixy*Ixy);
traceM = Ix2*Ix2 + Iy2*Iy2;
R = detM / traceM;
//cout <<i+j<< endl;
// std::cout << "R :" << Iy2 << endl;
if (R > threshold)
{
circle(image, cv::Point2f(i, j), 3.5, cv::Scalar(255, 255, 0), 1, 5);
cout << "corner found" << endl;
}
}
}
EDIT : i am using uchars now and the result looks alot better
2
I'm writing myself a Newton Fractal Generator. The images all looked like this:
But I actually would like it to look a bit smoother - sure I've done some research and I ran over http://www.hiddendimension.com/FractalMath/Convergent_Fractals_Main.html and this looks rather correct, except that there are at the edges of the basins some issues..
This is my generation loop:
while (i < 6000 && fabs(z.r) < 10000 && !found){
f = computeFunction(z, params, paramc[0]);
d = computeFunction(z, paramsD, paramc[1]);
iterexp = iterexp + exp(-fabs(z.r) - 0.5 / (fabs(subComplex(zo, z).r)));
zo = z;
z = subComplex(z, divComplex(f, d));
i++;
for (int j = 0; j < paramc[0] - 1; j++){
if (compComplex(z, zeros[j], RESOLUTION)){
resType[x + xRes * y] = j;
result[x + xRes * y] = iterexp;
found = true;
break;
}
}
if (compComplex(z, zo, RESOLUTION/100)){
resType[x + xRes * y] = 12;
break;
}
}
The coloration:
const int xRes = res[0];
const int yRes = res[1];
for (int y = 0; y < fraktal->getHeight(); y++){
for (int x = 0; x < fraktal->getWidth(); x++){
int type, it;
double conDiv;
if (genCL && genCL->err == CL_SUCCESS){
conDiv = genCL->result[x + y * xRes];
type = genCL->typeRes[x + y * xRes];
it = genCL->iterations[x + y * xRes];
} else {
type = 3;
conDiv = runNewton(std::complex<double>((double)((x - (double)(xRes / 2)) / zoom[0]), (double)((y - (double)(yRes / 2)) / zoom[1])), type);
}
if (type < 15){
Color col;
col.setColorHexRGB(colors[type]);
col.setColorHSV(col.getHue(), col.getSaturation(), 1-conDiv);
fraktal->setPixel(x, y, col);
} else {
fraktal->setPixel(x, y, conDiv, conDiv, conDiv, 1);
}
}
}
I appreciate any help to actually smooth this ;-)
Thanks,
- fodinabor
I'm implementing an algorithm, I excuse myself for the extreme for looping, haven't found a better way yet.
The problem is that at the second iteration at line 81 it gives a First-chance exception at 0x000000007707320E (ntdll.dll) in Test.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.
void co_hog(Mat image, int offset, int blockSize, int nrBins, int cat) {
Mat img_x;
Mat img_y;
IplImage img = image;
Mat kern_x = (Mat_<char>(1, 3) << -1, 0, 1);
Mat kern_y = (Mat_<char>(3, 1) << -1, 0, 1);
filter2D(image, img_x, image.depth(), kern_x);
filter2D(image, img_y, image.depth(), kern_y);
Size imageSize = image.size();
int nrBlocksY = imageSize.height / blockSize;
int nrBlocksX = imageSize.width / blockSize;
int degreePerBin = 180 / nrBins;
Mat gradients = Mat(image.size(), CV_32FC1);
Mat magnitudes = Mat(image.size(), CV_32FC1);
for(int y = 0; y < image.rows; y++) {
for(int x = 0; x < image.cols; x++) {
float grad_x = (float)img_x.at<uchar>(y, x);
float grad_y = (float)img_y.at<uchar>(y, x);
gradients.at<float>(y, x) = abs(atan2(grad_y, grad_x) * 180 / PI);
magnitudes.at<float>(y, x) = sqrt(pow(grad_x, 2) + pow(grad_y, 2));
}
}
int bin_1, bin_2, bin_3, bin_4;
double theta_1, theta_2, theta_3, theta_4;
Mat H;
stringstream line(stringstream::in | stringstream::out);
line << cat << " ";
int index = 1;
for(int i = 0; i < nrBlocksY; i++) {
for(int j = 0; j < nrBlocksX; j++) {
Mat coOccMat = Mat::zeros(nrBins, nrBins, CV_32FC1);
for(int q = i * blockSize; q < (i * blockSize) + blockSize; q++) {
for(int p = j * blockSize; p < (j * blockSize) + blockSize; p++) {
for(int offy = -offset; offy < offset; offy++) {
for(int offx = -offset; offx < offset; offx++) {
if((q + offy) >= imageSize.height || (p + offx) >= imageSize.width || (q + offy) < 0 || (p + offx) < 0) {
continue;
}
float m_1 = magnitudes.at<float>(q, p);
float m_2 = magnitudes.at<float>(q + offy, p + offx);
float alpha = gradients.at<float>(q, p);
float beta = gradients.at<float>(q + offy, p + offx);
if(fmod(alpha / degreePerBin, 1) > 0.5) {
bin_1 = floor(alpha / degreePerBin);
bin_2 = bin_1 + 1;
} else {
bin_2 = floor(alpha / degreePerBin);
bin_1 = bin_2 - 1;
}
if(fmod(beta / degreePerBin, 1) > 0.5) {
bin_3 = floor(beta / degreePerBin);
bin_4 = bin_3 + 1;
} else {
bin_4 = floor(beta / degreePerBin);
bin_3 = bin_4 - 1;
}
theta_1 = (bin_1 * degreePerBin) + (degreePerBin / 2);
theta_2 = (bin_2 * degreePerBin) + (degreePerBin / 2);
theta_3 = (bin_3 * degreePerBin) + (degreePerBin / 2);
theta_4 = (bin_4 * degreePerBin) + (degreePerBin / 2);
coOccMat.at<float>(bin_1, bin_3) += (m_1 * (1 - (alpha - theta_1) / (theta_2 - theta_1))) + (m_2 * (1 - (beta - theta_3) / (theta_4 - theta_1)));
coOccMat.at<float>(bin_1, bin_4) += (m_1 * (1 - (alpha - theta_1) / (theta_2 - theta_1))) + (m_2 * ((beta - theta_3) / (theta_4 - theta_1)));
coOccMat.at<float>(bin_2, bin_3) += (m_1 * ((alpha - theta_1) / (theta_2 - theta_1))) + (m_2 * (1 - (beta - theta_3) / (theta_4 - theta_1)));
coOccMat.at<float>(bin_2, bin_4) += (m_1 * ((alpha - theta_1) / (theta_2 - theta_1))) + (m_2 * ((beta - theta_3) / (theta_4 - theta_1)));
}
}
}
}
cout << coOccMat << endl;
-> Next statement to be called *passes the first time* H = coOccMat.reshape(0, 1);
normalize(H, H);
cout << H.size() << endl;
for(int i = 0; i < H.cols; ++i) {
for(int j = 0; j < H.rows; ++j) {
if(H.at<float>(j, i) > 0) {
line << index << ":" << H.at<float>(j, i) << " ";
}
index++;
}
}
cout << "Done" << index << endl;
}
}
}
Problem has been fixed, sometimes the value for a bin was set on -1 so it couldn't access it, debugging tools of visual studio couldn't point out where it went wrong.