The median filter takes in the neighbourhood the median color (see Median filter)

(to test the function below, you can use these input and output solutions)

-- demo\rosetta\Bitmap_Median_filter.exw
include ppm.e
 
constant neigh = {{-1,-1},{0,-1},{1,-1},
                  {-1, 0},{0, 0},{1, 0},
                  {-1, 1},{0, 1},{1, 1}}
 
--constant neigh = {{-2,-2},{-1,-2},{0,-2},{1,-2},{2,-2},
--                {-2,-1},{-1,-1},{0,-1},{1,-1},{2,-1},
--                {-2, 0},{-1, 0},{0, 0},{1, 0},{2, 0},
--                {-2, 1},{-1, 1},{0, 1},{1, 1},{2, 1},
--                {-2, 2},{-1, 2},{0, 2},{1, 2},{2, 2}}
 
sequence kn = repeat(0,length(neigh))
 
function median(sequence image)
    integer h = length(image),
            w = length(image[1])
    for i=1 to length(image) do
        for j=1 to length(image[i]) do
            integer n = 0, c, p, x, y
            for k=1 to length(neigh) do
                x = i+neigh[k][1]
                y = j+neigh[k][2]
                if  x>=1 and x<=h
                and y>=1 and y<=w then
                    n += 1
                    c = image[x,j]
                    p = n
                    while p>1 do
                        if c>kn[p-1] then exit end if
                        kn[p] = kn[p-1]
                        p -= 1
                    end while
                    kn[p] = c
                end if
            end for
            if and_bits(n,1) then
                c = kn[(n+1)/2]
            else
                c = floor((kn[n/2]+kn[n/2+1])/2)
            end if
            image[i,j] = c
        end for
    end for
    return image
end function
 
sequence img = read_ppm("Lena.ppm")
    img = median(img)
    write_ppm("LenaMedian.ppm",img)