Source wikipedia. The median filter is a non-linear digital filtering technique, often used to remove noise from an image or signal. In Soil_Segmentation.py is used as a pre-processing step to improve the results of edge detection (e.g otsu segmentation). Median filtering is very widely used in digital image processing because, under certain conditions, it preserves edges while removing noise, somewhat like the mean filter. However, it often does a better job than the mean filter of preserving useful detail in the image.
The main idea is to run through the signal entry by entry, replacing each entry with the median of neighboring entries. The pattern of neighbors is called the "window", which slides, entry by entry, over the entire signal. For one-dimensional signals, the most obvious window is just the first few preceding and following entries, whereas for two-dimensional (or higher-dimensional) data the window must include all entries within a given radius or ellipsoidal region.
This is not interesting for Soil_segmentation.py because the images are 2-dimensional signals.
1. allocate outputPixelValue[image width][image height]
2. allocate window[window width × window height]
3. edgex := (window width / 2) rounded down
4. edgey := (window height / 2) rounded down
for x from edgex to image width - edgex do
for y from edgey to image height - edgey do
i = 0
for fx from 0 to window width do
for fy from 0 to window height do
window[i] := inputPixelValue[x + fx - edgex][y + fy - edgey]
i := i + 1
sort entries in window[]
outputPixelValue[x][y] := window[window width * window height / 2]
In Soil_Segmentation.py numpy.median method is used: np.median(flatten_view, axis=3)
.
The median filter considers each pixel in the image in turn and looks at its nearby neighbors to decide whether or not it is representative of its surroundings. The pixel value is replaced with the median value of the neighboring pixel values. The median is calculated by first sorting all the pixel values from the surrounding neighborhood into numerical order and then replacing the pixel being considered with the middle pixel value (If the neighborhood under consideration contains an even number of pixels, the average of the two middle pixel values is used).
The median is a more robust average than the mean and so a single very unrepresentative pixel in a neighborhood will not affect the median value significantly: the median filter does not create new unrealistic pixel values when the filter straddles an edge.
- Store the pixel values of input image in an array.
- For each pixel value store all the neighbor pixel value including that cell in a new array (called window).
- Sort the window array.
- Median of window array is used to store output image pixel intensity.
2D Median filtering example using a 3 x 3 sampling window: Extending border values outside with values at the boundary.
The median filter preserves edge property while Gaussian filter does not. Edge preservation is an important property because edges are important for visual appearance. For edge preservation property median filter is widely used in digital image processing.
stored_image = readImage(input_image) // copying the input image in an array
The FPGA accelerator is written in Verilog. It is composed by a main actor compatible with the MDC tool. The whole accelerator, which consists of 7 actors connected together through FIFOs (one per in/out signal), receives a stream of pixels (8-bit) from an input FIFO and sends another stream of pixels back to an output FIFO.
The actor inputs are:
[7:0] in_px
the input pixel stream channel;[7:0] in_pivot
the value which is used to separate the input pixels and push them into lower, equal and larger buffers;[10:0] in_buff_size
[10:0] in_median_pos
[7:0] in_second_median_value
The outputs are:
[7:0] out_px
the next pixel stream channel;[7:0] out_pivot
the next pivot value which is used to separate the input pixels and push them into lower, equal and larger;[10:0] out_buff_size
[10:0] out_median_pos
[7:0] out_second_median_value
3 main modules:
fill_logic.v
next_logic.v
send_logic.v
Each actor has two main phases:
- Fill (
fill_logic
) - during this step the actor reads and samplesin_pivot
,in_buff_size
,in_median_pos
andin_second_median_value
, then it savesin_buff_size
pixels into temp buffers (lower, equal and larger thanin_pivot
). - Send (
send_logic
) - during this step the actor sends the pixels either from lower buffer or equal buffer or larger buffer, and sends the updatedout_pivot
,out_buff_size
,out_median_pos
andout_second_median_value
. Each of output value is chosen by thenext_logic
.
This module has the purpose to store the input pixels, from the previous actor, into three temporary buffers (lower, equal and larger) and then to signal the end of filling (fill_done
). Where saving the in_px
is chosen through the in_pivot
.
So, this module will be able to generate the rd
signals to read the input data from the FIFOs, sample them and the signals the fill_done
to the send_logic
. To do that, this logic:
- samples the input control values into registers (
in_pivot
,in_buff_size
,in_median_pos
andin_second_median_value
); - starts to fill the temporary buffers
- reads
in_buff_size
pixels from the FIFO - for each pixel read, it pushes the pixel into the proper buffer and updates the sizes, the max and min lower and larger values
- reads
- once the filling process ends it signals the end to the send logic - all the pixels are saved
- once the send logic receives the command and it is free, the fill logic could start a new filling. Therefore, it restarts the counters and buffers and repeats the process from step 1
- if the fill logic has ended filling and the send logic is busy, the fill logic waits the send logic and then continue to the step 3
It manages the whole filling process. It raises the read request properly, puts the logic in a wait status if needed and signals the filling status of the logic. The inputs are:
fill_done
- it signals that all the pixels are stored, hence the pixels are ready to be sent to the send logicsending
- it signals that the send logic is a sending status, so it is busy and it's not able to receive a new requestcontrol_sampled
- it signals that the control data are stored, therefore the reading process can start
The outputs are:
read_req
- it is a 1-bit signal which is sent to the input FIFO to read the pixels (*remember that infifo_small
a data is valid only if rd==1 and empty==0)filling
- it signals the status of the fill logic to the other modules, (e.g. if filling==1 the control signals cannot be read from the FIFOs)up_next
- update next, it allows the update of next data (data that should be sent to the next actor), the fill logic is restarted
The states:
- IDLE=2'b00 - the fill logic is neither filling nor waiting the send logic
- READ=2'b01 - the fill logic is reading from the input pixels FIFO (it is filling the temporary buffers)
- WAIT=2'b10 - the fill logic is waiting the send logic, that is fill logic ends filling but send logic is busy
- SEND_REQ=2'b11 - the fill logic raises a request send to the send logic, the pixels and the next control values are sent to send logic
The counters are updated when a new pixel is received and they are finally restarted once the send request has been done.
This module updates the next data needed by the send logic. It could be completely a combinatorial module but it has not any sense since the values must be sent only once the fill logic ends and send logic is not busy. Indeed, the next values are stored in registers and that registers are updated once the up_next
is equal to
This module handles the sending to the next actor of the next values. It is controlled by a fsm and output counter, as in the fill logic. The transmission starts only once the up_next
signal is raised (note: the 'up_next` is raised only if send logic is not actually already sending). Therefore, the send logic has to generate a sending (busy signal) properly.
It is quite simple and it manages the sending step. It receives the update trigger from the fill logic and put the logic in a sending status in which the sending requests are done. The inputs are:
up_next
- update next request, once this signal is received the next values are available, so they could be sent to the next actorsend_done
- this signal communicates that the last pixel has been sent
The outputs are:
sending
- the busy signal, it is sent to the fill logic and it communicates that the send logic is busy and, therefore, it cannot received other requests (up_next==1)send_req
- it is sent to the output FIFO (connected to wr FIFO's port)samp_data
- this signal makes the send logic able to save store the next data, *but it might not necessary because the next logic itself is composed by registers and such registers are updated only if up_next==1 by the next logic and, since up_next could be 1 only if sending==0, all data are ever true
The states:
- IDLE=1'b0 - the send logic is not sending and no pixels are sent to the next actor
- SEND=1'b1 - the send logic is sending the next buffer's pixels