Edge Detection (Image Processing) Part 3

C6701 EVM Target

A target project meant to be used with C62xx or C67xx EVM boards can be found in Chap5\edge\SobelFixedRTDX\Target\C6701EVM. This project has been tested on the C6701 EVM, and is quite similar to the C6416 DSK project. Since the C6701 EVM’s memory map differs from that of the C6416 DSK, the DSP/BIOS MEM module configuration reflects these differences, which can be discerned by comparing two linker command files for each development platform. The RTDX configuration also differs, in that the . rtdx_data segment (see Figure 5-11) is mapped to external RAM due to the smaller amount of space available on the C62xx/C67xx DSPs, as compared to the more powerful C6416. Such a configuration affects RTDX throughput, because of the latencies involved in accessing the external memory. Due to these timing changes, the target application does need to change to account for this difference. Moreover, on the C62xx/C76xx EVM platform setting the RTDX buffer size to values greater than 64K results in RTDX completely failing to function correctly, and thus in this project the buffer size is set to 65536 (versus 128K as was done in the C6416 version).

The timing changes manifest themselves in a change to how the processed pixel data is written back to the host client application. Basically, a failure to insert a pause when writing the image rows to the RTDX output channel ochan results in RTDX_write returning an error just prior to sending the last few rows of image data. Therefore, an artificial pause is inserted after having sent half of the image rows, in order to give the host client application time to read some of the image data and flush out the RTDX buffers. A better means would be to somehow query the RTDX state programmatically, and temporarily stop writing data to the output channel until it has been determined that the client has had a chance to read enough of the data so that subsequent calls to RTDX_write succeed. Unfortunately, there appears to be no easy way of implementing such a strategy.


The question now becomes how exactly to implement this pause? On POSIX and Windows systems there is a function sleep (UNIX) or Sleep (Win32) that can be used to suspend a running process. These operating systems are not real-time, and thus one is guaranteed only that the suspended process shall be halted for at least the amount of time specified in the sleep or Sleep call. It is entirely possible that the suspended process will be halted for longer than that amount of time. DSP/BIOS provides a function TSK_sleep which fits the bill, suspending the currently running task for "exactly" the specified amount of time, where "exactly" depends on the clock resolution and whether there are higher priority tasks waiting to run after the task in question is ready to resume. None of our embedded DSP code thus far has utilized separate tasks, rather the programs have been of a fairly simple variety and called a few functions from main, or as in the previous program’s case, executed in an infinite loop within main. In the C6701 EVM Sobel project, the DSP/BIOS configuration tool is used to create a task which hosts this infinite loop. This task can then be suspended at the right time in order to allow the host client application time to catch up.

Figure 5-12 is a screen-shot from the DSP/BIOS configuration tool illustrating how to create a new task and incorporate it into a project. The entry-point function, where a DSP/BIOS task begins its life, is specified in the "Task function" field, and the function name (if it is implemented in C) must be preceded by an underscore, otherwise the build fails at link time because the linker decorates all C function names with a preceding underscore.

Listing 5-4 is a portion of the C6701 sobel_edge_detect. c source code (the remainder is identical to the C6416 version), showing that main is now mostly empty. The DSP/BIOS kernel starts the new processingTsk task at startup, which sends the program flow into the processing task function. Most tasks follow a simple motif: an infinite loop that performs some processing or executes a series of commands when asked to from some outside entity. This task is no different, and in fact its internals are quite similar to that of the main function in Listing 5-3, except for the portion of code that sends the processed data back to the host client. After the program has transferred half of the processed edge image rows, TSK_sleep is called to suspend the task for 1000 clock ticks, thereby giving the client the opportunity to read the data the target has already written and flush the RTDX buffers.

Using DSP/BIOS to add a new task to a project. The other tabs in the task properties dialog have settings for a variety of other options, such as the location of the task's stack and its size and the task priority.

Figure 5-12. Using DSP/BIOS to add a new task to a project. The other tabs in the task properties dialog have settings for a variety of other options, such as the location of the task’s stack and its size and the task priority.

If there was another lower-priority task waiting to run, the DSP/BIOS scheduler would allow this task to execute during processingTsk’s suspension. Such determinism is in stark contrast to general-purpose non-real-time operating systems like Windows or Linux. In those operating systems, the scheduler is typically of a "round-robin" nature, and the scheduler’s charter is to guarantee fairness so that no process is starved of CPU time, as opposed to a hard real-time system like DSP/BIOS where the overriding goal is to ensure deterministic behavior.

Listing 5-4: The C6701 EVM version of sobel_edge_detect.c. This version differs from that of Listing 5-3 in that it uses a DSP/BIOS task, and during the write operation this task is suspended so that the RTDX operation succeeds.

Listing 5-4: The C6701 EVM version of sobel_edge_detect.c. This version differs from that of Listing 5-3 in that it uses a DSP/BIOS task, and during the write operation this task is suspended so that the RTDX operation succeeds.

 

 

 

 

 

Listing 5-4: The C6701 EVM version of sobel_edge_detect.c. This version differs from that of Listing 5-3 in that it uses a DSP/BIOS task, and during the write operation this task is suspended so that the RTDX operation succeeds.

Host MATLAB Application

The host application that provides a front-end user interface for both targets takes the form of a modified version of the MATLAB Sobel demo application, and is located in Chap5\edge\SobelFixedRTDX\Host. As before, the MATLAB source code for the application resides in SobelDemo .m. The GUI behavior remains the same, but underneath MATLAB makes a connection to Code Composer Studio via the Link for Code Composer Studio, and subsequently uses this connection to feed data to the target and read processed data from the target over RTDX channels.

Depending on the target, the only change that needs to be made to the target source code is to modify a single string variable that points the application to a directory on the host from which the COFF binary file is loaded onto the DSP. This type of initialization code is usually placed within a startup subfiinction that the GUIDE tool automatically creates whenever one builds a MATLAB GUI application. Listing 5-5 shows the contents of this subfunction, SobelDemo_OpeningFcn.

Listing 5-5: MATLAB GUI initialization code, which establishes a connection to CCStudio and configures RTDX for image data transfer.

% — Executes just before SobelDemo is made visible, function SobelDemo_OpeningFcn(hObject, eventdata, handles, varargin)

% This function has no output args, see OutputFcn.

% hObject handle to figure

% eventdata reserved – to be defined in a future version of MATLAB

% handles structure with handles and user data (see GUIDATA)

Listing 5-5: MATLAB GUI initialization code, which establishes a connection to CCStudio and configures RTDX for image data transfer.

 

 

 

 

Listing 5-5: MATLAB GUI initialization code, which establishes a connection to CCStudio and configures RTDX for image data transfer.

The handles data structure is where common data, used throughout the GUI, should be placed so that all callback subfunctions have access to it. Most importantly for this discussion, handles, cc is a Link for Code Composer Studio object through which a connection is made to CCStudio and RTDX data transfers take place. After the binary executable is loaded onto the target DSP by invoking various toolbox functions, the host side of RTDX is configured and enabled. After the size of the host RTDX buffer is set, the host RTDX channels are then configured. The open method creates a read-only or write-only RTDX channel and associates it with the name given as the first argument. The naming conventions used here are based on the target’s point-of-view; that is, the channel over which MATLAB sends data to the DSP is given the name 1 ichan’, because with respect to the target this is the input channel. Likewise, the reverse direction (MATLAB reading processed data emanating from the target) is given the name ‘ochan’.

The other area in the host application where the Link for Code Composer Studio is utilized is in the updated callback for the "Edge Detect" button, shown in Listing 5-6. Because the target is expecting a 256×256 image, the image the user initially loaded in from the File menu is resized to those dimensions using imresize prior to sending it down to the target.

Listing 5-6: MATLAB callback subfunction for the "Edge Detect" button, which sends an image down to the target and then reads and displays the processed image.

% — Executes on button press in edge_detect_button. function edge_detect_button_Callback(hObject, eventdata, handles)

% hObject handle to edge_detect_button (see GCBO)

% eventdata reserved – to be defined in a future version of MATLAB

% handles structure with handles and user data (see GUIDATA)

T = str2num(get(handles.threshold_edit, ‘String’));

% get pointer to image data – I could just have easily created % another field in the handles struct, but since it is held by a

% child of the axes object, in the interest of reducing clutter I % access it this way.

Listing 5-6: MATLAB callback subfunction for the "Edge Detect" button, which sends an image down to the target and then reads and displays the processed image.

 

 

 

 

Listing 5-6: MATLAB callback subfunction for the "Edge Detect" button, which sends an image down to the target and then reads and displays the processed image.

 

Because RTDX can accommodate a 256×256 byte host-to-target transfer, a single call to the writemsg Link for Code Composer function suffices to send pixel data to the target. However, care must be taken to transpose the matrix prior to flattening the two-dimensional array due to the differences in data layout between MATLAB and the target. Recall that in MATLAB, multi-dimensional arrays are stored in column-major format while the C language is row-major in nature. In addition, an explicit cast to the uint8 data type is necessary to guard against the case where J is not of type uint 8, in which case the result would be extraneous data sent to the target.

For reasons discussed previously, reading the processed image data from the target is more involved, since it cannot be performed with a single function call. Once the edge detection completes, the target sends back 256 rows, or more strictly speaking 256 RTDX messages. Reading all of the processed data from the target is accomplished via a while loop that queries RTDX via msgcount for how many messages are sitting in a FIFO (first-in, first-out), and then comparing this count to how many image rows are needed to completely fill an image matrix. There is some post-processing of the data required due to the implementation details behind the IMGLIB functions used to implement Sobel edge detection on the target. The first and last columns are meaningless, and are therefore set to zero. Similarly, the valid image data is actually two rows less than the original image, and so those rows are also set to zero. Finally, the target does not send back a binary image per se, but rather the way the target is implemented any nonzero pixel should be interpreted as an edge, with a binary value of 1 or true. The built-in MATLAB function logical is used to transform the edge image into a binary image consisting of just ones and zeros.

Ideas for Further Improvement

The motivation behind this project was two-fold: first, to show how edge detection can be easily implemented on TI DSPs using IMGLIB functions, and second, to introduce RTDX and present a MATLAB host application that can be used as a template for a complete interactive image processing application. That being said, there remains much room for improvement with regards to this particular application, in terms of functionality as well as performance.

With regards to functionality, the restriction of a fixed image size is rather limiting. A more useful application would allow the user to read in any image and feed it to the DSP to be processed, as opposed to forcing a 256×256 size on the user. Of course, if the edge detection is part of a larger system (say for example a video surveillance product) the image (or individual video frames, as the case may be) is always going to be a certain size and this point is moot. But for testing purposes, a useful addition would be to change the protocol between the host and target such that the host sends the target the image dimensions, and then the target adjusts its internal algorithms appropriately. There are some limitations that must be always be accounted for, such as a maximum image size and power-of-2 considerations for the various IMGLIB functions.

The target performance can be dramatically improved by utilizing paging and DMA to send image blocks into internal chip memory. This technique has been described in full detail in next topic, and here the DSP/BIOS DMA module would prove useful – a DMA Application Programming Interface (API) within DSP/BIOS exists that roughly mirrors that of the CSL library.

Finally, collapsing the Sobel edge detection and image thresholding into a single loop will further increase performance. Currently, the edge image is formed by first calling IMG sobel and following that with a call to IMG_thr_le2min. By performing the filtering and thresholding within a single nested loop, a la Algorithm 5-1, the looping overhead is reduced. To garner maximal performance out of the algorithm, the loop kernel would need to be fully optimized, which has already been done separately for us in the two IMGLIB functions. More information on how to implement an efficient threshold loop kernel on the C64x platform can be found in [15].

Next post:

Previous post: