Sobel Operator C - Edge detection gone wrong - c

I ve been trying hard to calculate the gradient (and dips) of a binary image in C using the Sobel operator. I ve already checked the operators several times and went through tons of internet sites. Nevertheless, I have to admit that I have no experience with image processing and I am quite a rookie in C coding. I dont get any error messages, but the result does not show the desired gradients on the edges.
Somehow the gradient in the x-direction is not calculated - but why?
Thanks for the help!
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
FILE *infile, *outfile;
int main(int argc, char *argv[])
{
int nx,nz,k,i,nu;
int j,m,l;
int km, kp, im, ip, kpp, ipp;
float Gx, Gz, G, Gmax, Gmin;
float T;
float **dip;
float pi;
float *tmp;
float *bufz;
float **temp1;
char *velfile_in = "vel";//"vel_315_273";
char *velfile_out = "dip";
void *alloc1 (size_t n1, size_t size);
void **alloc2(size_t n1, size_t n2, size_t size);
void ***alloc3(size_t n1, size_t n2, size_t n3, size_t size);
pi = 4. * atan(1.);
// Initiate constants
T = atof(argv[1]);
nx = atoi(argv[2]);
nz = atoi(argv[3]);
Gmax = 0.;
Gmin = 10e8;
// border handling (cyclic)
km = (k+nz-1) % nz;
kp = (k+1) % nz;
kpp = (k+2) % nz;
im = (i+nx-1) % nx;
ip = (i+1) % nx;
ipp = (i+2) % nx;
// allocate 1D, 2D and 3D arrays
tmp = (float *)alloc1(nz,sizeof(float));
bufz = (float *)alloc1(nz,sizeof(float));
temp1 = (float **)alloc2(nx,nz,sizeof(float));
dip = (float **)alloc2(nx,nz,sizeof(float));
//READ FILE
//***********************************************************
infile = fopen(velfile_in, "r");
if (infile == NULL) err("Error: could not open file.");
for (i=0; i<nx; i++) {
nu = fread(tmp,sizeof(float),nz,infile);
for (k=0; k<nz; k++) {
temp1[k][i] = tmp[k];
}
}
fclose(infile);
// APPLY SOBEL****************************************
for (i = 0; i < nx; i++)
{
for (k = 0; k < nz; k++)
{
Gx = (temp1[km][im] - temp1[km][ip] + 2 * temp1[k][im] - 2 * temp1[k][ip] + temp1[kp][im] - temp1[kp][ip]);
Gz = (temp1[km][im] - temp1[kp][im] + 2 * temp1[km][i] - 2 * temp1[kp][i] + temp1[km][ip] - temp1[kp][ip]);
G = sqrtf(Gx * Gx + Gz * Gz);
Gmax = (Gmax > G ? Gmax : G);
Gmin = (Gmin < G ? Gmin : G);
dip[k][i] = abs(atan(Gz/Gx) * 180. / pi);
printf("(%d,%d)\tGx:%5.3f\tGz%5.3f\tG%5.3f\n",i,k,Gx,Gz,G);
}
}
printf("Gmax:%5.3f\tGmin:%5.3f\n",Gmax,Gmin);
// write file ********************************************************
outfile = fopen(velfile_out,"w");
for (i=0; i<nx; i++) {
for (k=0; k<nz; k++) bufz[k] = dip[k][i];
fwrite(bufz,sizeof(float),nz,outfile);
}
fclose(outfile);
return 0;
}

The obvious problem is that your convolution kernel isn't moving - the array indices are all separate variables and need updating to stay in sync with i and j. Moving the assignments inside the loops should fix that:
...
for (i = 0; i < nx; i++)
{
im = (i+nx-1) % nx;
ip = (i+1) % nx;
ipp = (i+2) % nx;
for (k = 0; k < nz; k++)
{
km = (k+nz-1) % nz;
kp = (k+1) % nz;
kpp = (k+2) % nz;
...

Related

As a result of processing arrays -nan(ind)

I am writing a program that creates arrays of a given length and manipulates them. You cannot use other libraries.
First, an array M1 of length N is formed, after which an array M2 of length N is formed/2.
In the M1 array, the division by Pi operation is applied to each element, followed by elevation to the third power.
Then, in the M2 array, each element is alternately added to the previous one, and the tangent modulus operation is applied to the result of addition.
After that, exponentiation is applied to all elements of the M1 and M2 array with the same indexes and the resulting array is sorted by dwarf sorting.
And at the end, the sum of the sines of the elements of the M2 array is calculated, which, when divided by the minimum non-zero element of the M2 array, give an even number.
The problem is that the result X gives is -nan(ind). I can't figure out exactly where the error is.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
const int A = 441;
const double PI = 3.1415926535897931159979635;
inline void dwarf_sort(double* array, int size) {
size_t i = 1;
while (i < size) {
if (i == 0) {
i = 1;
}
if (array[i - 1] <= array[i]) {
++i;
}
else
{
long tmp = array[i];
array[i] = array[i - 1];
array[i - 1] = tmp;
--i;
}
}
}
inline double reduce(double* array, int size) {
size_t i;
double min = RAND_MAX, sum = 0;
for (i = 0; i < size; ++i) {
if (array[i] < min && array[i] != 0) {
min = array[i];
}
}
for (i = 0; i < size; ++i) {
if ((int)(array[i] / min) % 2 == 0) {
sum += sin(array[i]);
}
}
return sum;
}
int main(int argc, char* argv[])
{
int i, N, j;
double* M1 = NULL, * M2 = NULL, * M2_copy = NULL;
double X;
unsigned int seed = 0;
N = atoi(argv[1]); /* N равен первому параметру командной строки */
M1 = malloc(N * sizeof(double));
M2 = malloc(N / 2 * sizeof(double));
M2_copy = malloc(N / 2 * sizeof(double));
for (i = 0; i < 100; i++)
{
seed = i;
srand(i);
/*generate*/
for (j = 0; j < N; ++j) {
M1[j] = (rand_r(&seed) % A) + 1;
}
for (j = 0; j < N / 2; ++j) {
M2[j] = (rand_r(&seed) % (10 * A)) + 1;
}
/*map*/
for (j = 0; j < N; ++j)
{
M1[j] = pow(M1[j] / PI, 3);
}
for (j = 0; j < N / 2; ++j) {
M2_copy[j] = M2[j];
}
M2[0] = fabs(tan(M2_copy[0]));
for (j = 0; j < N / 2; ++j) {
M2[j] = fabs(tan(M2[j] + M2_copy[j]));
}
/*merge*/
for (j = 0; j < N / 2; ++j) {
M2[j] = pow(M1[j], M2[j]);
}
/*sort*/
dwarf_sort(M2, N / 2);
/*sort*/
X = reduce(M2, N / 2);
}
printf("\nN=%d.\n", N);
printf("X=%f\n", X);
return 0;
}
Knowledgeable people, does anyone see where my mistake is? I think I'm putting the wrong data types to the variables, but I still can't solve the problem.
Replace the /* merge */ part with this:
/*merge*/
for (j = 0; j < N / 2; ++j) {
printf("%f %f ", M1[j], M2[j]);
M2[j] = pow(M1[j], M2[j]);
printf("%f\n", M2[j]);
}
This will print the values and the results of the pow operation. You'll see that some of these values are huge resulting in an capacity overflow of double.
Something like pow(593419.97, 31.80) will not end well.

Writing a C array to 3D volume in vtk format using array strides and grid spacing

Suppose I have an array of floating points (malloced) and this array represents a 3d volume of nx * ny * nz with strides in the respective directions as sx = 1, sy = nx and sz = nx * ny. The grid spacing is say some constant dx. How can I write this data into a vtk file (I suppose vtkImage should be the format I should look at) for visualization in Paraview?
A minimal example for the data arrangement as described above is as follows:
#include <math.h>
#include <stdio.h>
int main()
{
/* array dim */
int nx = 512;
int ny = 512;
int nz = 512;
long int nt = nx * ny * nz; /* total values */
/* strides */
int sx = 1;
int sy = nx;
int sz = nx * ny;
/* grid spacing (can be different as well, in which case rectillinear grid should be needed) */
dx = 40.;
dy = 40.;
dz = 40.;
/* allocation */
float* data = malloc(nx * ny * nz * sizeof(float));
/* fill up values */
for (int k = 0; k < nz; k++)
for (int j = 0; j < ny; j++)
for (int i = 0; i < nx; i++) {
data[sx * i + sy * j + sz * k ] = 5.0; // constant but could be a mathematical function
}
/* how to write this array to a vtk file ? */
return 0;
}

Exception thrown, Dynamic memory allocation in C

Hi I'm trying to do dynamic memory allocation of a large matrix in C but I'm running into the following error:
Exception thrown at 0x00007FF63A248571 in cdempd.exe: 0xC0000005: Access violation writing location 0x0000000000000000. occurred
sometimes it's Access violation writing location 0xFFFFFFFFB412E2A0.
double ndivx, ndivy, ndivz, nt, r, box, dx, totnode;
int main()
{
ndivx = 19.0;
ndivy = 19.0;
ndivz = 19.0;
int totnode = ndivx * ndivy * ndivz;
r = 0.005; //diameter of sphere
dx = 0.0025 / ndivx;
double dx = r / ndivx; // distance between points
int cols = 3;
int** coords;
coords = malloc(totnode * sizeof(int*));
for (int i = 0; i < totnode; i++) {
coords[i] = malloc(cols * sizeof(int));
}
//int* coord = (int*)malloc(totnode * cols * sizeof(int));
// int offset = i * cols + j;
// now mat[offset] corresponds to m(i, j)
//create a cube of equidistant points
int numm = 0;
for (int i = 1; i <= ndivx; i++)
{
for (int j = 1; j <= ndivy; j++)
{
for (int k = 1; k <= ndivz; k++)
{
coords[numm][0] = -1.0 / 2.0 * (r)+(dx / 2.0) + (i - 1.0) * dx;
coords[numm][1] = -1.0 / 2.0 * (r)+(dx / 2.0) + (j - 1.0) * dx;
coords[numm][2] = -1.0 / 2.0 * (r)+(dx / 2.0) + (k - 1.0) * dx;
numm = numm + 1;
}
}
}
}
pd.r is a double 0.005, dx is a double about 0.00026315, totnode is 6859.
I've tried two methods, the one that is there and the one commented out with //. Both give me the same error. I'm using visual studio 2019. I'm not so familiar with c and visual studio so forgive me if the question is silly. Any help would be appreciated thank you.
Aside from some of the other errors [after correction], all values of coords are set to zero. This is because coords is a pointer to int and not (e.g.) double and your equation uses -1.0 / ... which will always produce a fraction.
Also, as David pointed out, you're indexing from 1 [vs. 0] in the for loops. This could cause access violations/segfaults.
I've changed the for loops to start from 0. And, I've adjusted the equation accordingly (using a macro).
You were defining some things like index variables or size variables as double instead of int (e.g.) ndivx
Also, I introduced a typedef for the coordinate values.
Here's some cleaned up code that may help get you further:
#include <stdio.h>
#include <stdlib.h>
#if 0
double ndivx, ndivy, ndivz, nt, r, box, dx, totnode;
#endif
#if 0
typedef int coord_t;
#else
typedef double coord_t;
#endif
#define SETCOORD(_xidx,_var) \
do { \
coords[numm][_xidx] = -1.0 / 2.0 * r + (dx / 2.0) + (_var * dx); \
printf("coords[%d][%d]=%g\n",numm,_xidx,(double) coords[numm][_xidx]); \
} while (0)
int
main(void)
{
#if 1
int ndivx;
int ndivy;
int ndivz;
double r;
double dx;
#endif
ndivx = 19;
ndivy = 19;
ndivz = 19;
int totnode = ndivx * ndivy * ndivz;
r = 0.005; // diameter of sphere
dx = 0.0025 / ndivx;
#if 0
double dx = r / ndivx; // distance between points
#else
dx = r / ndivx; // distance between points
#endif
int cols = 3;
#if 0
int **coords;
#else
coord_t **coords;
#endif
coords = malloc(totnode * sizeof(coord_t *));
for (int i = 0; i < totnode; i++) {
coords[i] = malloc(cols * sizeof(coord_t));
}
// int* coord = (int*)malloc(totnode * cols * sizeof(int));
// int offset = i * cols + j;
// now mat[offset] corresponds to m(i, j)
// create a cube of equidistant points
int numm = 0;
for (int i = 0; i < ndivx; i++) {
for (int j = 0; j < ndivy; j++) {
for (int k = 0; k < ndivz; k++) {
SETCOORD(0,i);
SETCOORD(1,j);
SETCOORD(2,k);
numm = numm + 1;
}
}
}
return 0;
}

DGEMM and DGEMV give different results

I want to implement the following equation in C:
C[l,q,m] = A[m,q,k] * B[k,l]
where the repeated index k is being summed over.
I implemented this in three ways:
Naive implementation with loops
Using the BLAS routine DGEMV (matrix-vector multiplication)
Using the BLAS routine DGEMM (matrix-matrix multiplication)
This is the minimal not-working code:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <cblas.h>
void main()
{
const size_t n = 3;
const size_t n2 = n*n;
const size_t n3 = n*n*n;
/* Fill rank 3 tensor with random numbers */
double a[n3];
for (size_t i = 0; i < n3; i++) {
a[i] = (double) rand() / RAND_MAX;
}
/* Fill matrix with random numbers */
double b[n2];
for (size_t i = 0; i < n2; i++) {
b[i] = (double) rand() / RAND_MAX;
}
/* All loops */
double c_exact[n3];
memset(c_exact, 0, n3 * sizeof(double));
for (size_t l = 0; l < n; l++) {
for (size_t q = 0; q < n; q++) {
for (size_t m = 0; m < n; m++) {
for (size_t k = 0; k < n; k++) {
c_exact[l*n2+q*n+m] += a[m*n2+q*n+k] * b[k*n+l];
}
}
}
}
/* Matrix-vector */
double c_mv[n3];
memset(c_mv, 0, n3 * sizeof(double));
for (size_t m = 0; m < n; m++) {
for (size_t l = 0; l < n; l++) {
cblas_dgemv(
CblasRowMajor, CblasNoTrans, n, n, 1.0, &a[m*n2],
n, &b[l], n, 0.0, &c_mv[l*n2+m], n);
}
}
/* Matrix-matrix */
double c_mm[n3];
memset(c_mm, 0, n3 * sizeof(double));
for (size_t m = 0; m < n; m++) {
cblas_dgemm(
CblasRowMajor, CblasTrans, CblasTrans, n, n, n, 1.0, b, n,
&a[m*n2], n, 0.0, &c_mm[m], n2);
}
/* Compute difference */
double diff_mv = 0.0;
double diff_mm = 0.0;
for (size_t idx = 0; idx < n3; idx++) {
diff_mv += c_mv[idx] - c_exact[idx];
diff_mm += c_mm[idx] - c_exact[idx];
}
printf("Difference matrix-vector: %e\n", diff_mv);
printf("Difference matrix-matrix: %e\n", diff_mm);
}
And this the output:
Difference matrix-vector: 0.000000e+00
Difference matrix-matrix: -1.188678e+01
i.e. the DGEMV implementation is correct, the DGEMM not - I really don't understand this. I switched around the multiplication (matrix-matrix multiplication is non commutative) and transposed both to get the right order C[l,q,m] instead of C[q,l,m], but I also tried it without switching/transposing and it does not work.
Can anyone please help?
Thanks.
edit: I thought about it a bit and feel like I'm trying to do something that DGEMM doe not support? Namely I try to insert a submatrix into C[:,:,m], which means that both the leading and trailing index are not contiguous in memory. DGEMM allows me to set the parameter LDC, which in this case needs to be n^2, but it does not know that also the second index is non-contiguous with a stride of n (and there is no parameter to tell it?). So why does DGEMM not support a second parameter for the stride of the trailing dimension?

How can I improve locality of reads and writes in the following code?

I'm working on the following image convolution code:
typedef struct fmatrix{
int rows;
int cols;
float** array;
} fmatrix;
typedef struct image{
unsigned char* data;
int w;
int h;
int c;
} image;
typedef struct kernel{
fmatrix* psf;
int divisor;
} kernel;
void convolve_sq(image* src, image* dst, kernel* psf, int pixel){
int size = psf->psf->rows * psf->psf->cols;
float tmp[size];
int n, m; //for psf
int x, y, x0, y0, cur; //for image
y0 = pixel / (src->w * src->c);
x0 = (pixel / src->c) % src->w;
for (n = 0; n < psf->psf->rows; ++n){
for (m = 0; m < psf->psf->cols; ++m){
y = n - (psf->psf->rows / 2);
x = m - (psf->psf->cols / 2);
if ((y + y0) < 0 || (y + y0) >= src->h || (x + x0) < 0 || (x + x0) >= src->w){
tmp[n*psf->psf->rows+m] = 255 * psf->psf->array[n][m];
}
else{
cur = (pixel + y * src->w * src->c + x * src->c);
tmp[n*psf->psf->rows+m] = src->data[cur] * psf->psf->array[n][m]; //misses on read
}
}
}
m = 0;
for (n = 0; n < size; ++n){
m += (int) tmp[n];
}
m /= psf->divisor;
if (m < 0) m = 0;
if (m > 255) m = 255;
dst->data[pixel] = m; //misses on write
}
void convolve_image(image* src, image* dst, kernel* psf){
int i, j, k;
for (i = 0; i < src->h; ++i){
for (j = 0; j < src->w; ++j){
for (k = 0; k < src->c; ++k){
convolve_sq(src, dst, psf, (i * src->w * src->c + j * src->c + k) );
}
}
}
}
Running cachegrind, I've determined two places where there are a substantial number of cache misses, which I've annotated in the code above. For the line marked "misses on read", there were 97,205 D1mr and 97,201 DLmr. For the line marked "misses on write", there were 97,201 D1mw and DLmw. These lines read and write directly to/from the image respectively.
How can I make this code more efficient, in terms of avoiding cache misses?

Resources