Translating code from another language to Julia isn’t hard, especially so if that language is Matlab or even Python. I have been translating code from Python, and the biggest issues I have found are with code having array starting values of 0, versus Julia’s 1, and some of Pythons more eclectic ways of expressing things. Similarly with C.
Consider the case of a point detector, a simple filter to find discontinuities in an image. It basically applies a mask to all the pixels in the image, and the result is an image where the edges have been enhanced. Here is what the mask looks like:
Here is an example of a piece of C code and its equivalent in Julia. Here is the code in C:
void PointDetector(struct image pI, struct image *pO)
{
int x, y, i, j, sum;
int mask[3][3] = {{-1,-1,-1}, {-1,8,-1}, {-1,-1,-1}};
// Allocate space for the new image
pO->pixel = malloc(pI.nrows*sizeof(int *));
for (i=0; i<pI.nrows; i=i+1)
pO->pixel[i] = (int *)malloc(pI.ncols*sizeof(int *));
pO->nrows = pI.nrows;
pO->ncols = pI.ncols;
// Copy the pixels from the original image (edge pixels)
for (x=0; x<pI.nrows; x++)
for (y=0; y<pI.ncols; y++)
pO->pixel[x][y] = pI.pixel[x][y];
// Filter the image using the mask
for (x=1; x<pI.nrows-1; x++)
for (y=1; y<pI.ncols-1; y++){
sum = 0;
for (i=-1;i<=1;i++)
for (j=-1;j<=1;j++)
sum = sum + pI.pixel[x+i][y+j] * mask[i+1][j+1];
if (sum > 255)
sum = 255;
else if (sum < 0)
sum = 0;
pO->pixel[x][y] = sum;
}
}
Due to the potentially large size of an image, a dynamic array is used to store the image, contained within a struct that also holds values for the number of rows and columns in the image.
struct image
{
int nrows;
int ncols;
int **pixel;
};
Now consider the function translated into Julia:
function pointDetector(img)
dx,dy = size(img)
mask = [-1 -1 -1; -1 8 -1; -1 -1 -1]
imgP = copy(img)
for i=2:dx-1, j=2:dy-1
block = img[i-1:i+1,j-1:j+1]
conv = block .* mask
sumb = sum(conv)
if sumb > 255
sumb = 255
elseif sumb < 0
sumb = 0
end
imgP[i,j] = sumb
end
return imgP
end
The Julia function is much simpler. this stems partially from the simpler nature of the structure used to store the image (a simple array, with transparent storage), and partially from the lack of additional loops to deal with simple things like copying the image. The C function uses 7 for loops, the Julia function uses one (technically a nested loop). Copying the image is simpler, and does not require a structure which incorporate the dimensions of the array (again, transparent). Extraction of the “block” is achieved through array slicing, and convolution of the block with the mask is by means of element-wise multiplication. Finally, the sum uses the built-in function sum(). There is also no need to deal with dynamic arrays. I don’t have anything against dynamic arrays in C, but they do make code more “complex” looking, and they are a pain to debug. Also notice that depending on whether the struct image is passed by “value” (pI) or “reference” (pO), they uses different notation, “.” or “->” respectively (and this is just annoying).
So this translation was easy – not to say they all will be, but a good experience with less overhead.
And if you are wondering what the function actually does? Here is an example.
The image on the left is the original, the one on the right is after processing with the point detector algorithm.