Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Лишние NOP в ассемблерном коде
Форум разработчиков электроники ELECTRONIX.ru > Цифровая обработка сигналов - ЦОС (DSP) > Алгоритмы ЦОС (DSP)
Degun
Code Composer Studio v 3.3; Процессор DM642 на evalution board.
Имеется программка, написанная на C++. После анализа её ассемблерного кода выяснилось, что после многих команд (LDW, ADD, CMPLT, AND, LDHU и др.) компилятор вставляет команду NOP, что естественно замедляет функционирование программы. Для чего это? Можно ли отключить вставку операторов NOP в ассемблерный код?
fontp
Цитата(Degun @ Oct 23 2007, 15:54) *
Code Composer Studio v 3.3; Процессор DM642 на evalution board.
Имеется программка, написанная на C++. После анализа её ассемблерного кода выяснилось, что после многих команд (LDW, ADD, CMPLT, AND, LDHU и др.) компилятор вставляет команду NOP, что естественно замедляет функционирование программы. Для чего это? Можно ли отключить вставку операторов NOP в ассемблерный код?


Это не ассемблерный код, а выход С-компилятора.

У TI такой подход, что программист (или компилятор) должен следить за длительностью выполнения инструкций (latency). Например, если умножение требует 5 тактов, то нужно пять тактов и ждать, в противном случае Вы выхватываете из конвейера неверный результат (что, однако, никто Вам запретить не может). Вот компилятор и борется с "открытым конвейером"

Есть единственный гарантированый способ избежать вставки NOP в ассемблерный код - написать самому на ассемблере, так чтобы без NOPов ;-) TMS серии 6х - это ведь кубик Рубика
Degun
Цитата(fontp @ Oct 23 2007, 16:04) *
Это не ассемблерный код, а выход С-компилятора.

А разве это не одно и тоже?
Цитата(fontp @ Oct 23 2007, 16:04) *
У TI такой подход, что программист (или компилятор) должен следить за длительностью выполнения инструкций (latency). Например, если умножение требует 5 тактов, то нужно пять тактов и ждать, в противном случае Вы выхватываете из конвейера неверный результат (что, однако, никто Вам запретить не может). Вот компилятор и борется с "открытым конвейером"
Есть единственный гарантированый способ избежать вставки NOP в ассемблерный код - написать самому на ассемблере, так чтобы без NOPов ;-) TMS серии 6х - это ведь кубик Рубика

Не совсем понятно. Если умножению, например, требуется 5 тактов, то соответствующая команда и будет выполняться необходимое кол-во тактов и только после после этого перейдёт к выполнению следующей. Или переход к выполнению следующей команды осуществляется сразу, а результат умножения будет готов только через 5 тактов?
fontp
Цитата(Degun @ Oct 23 2007, 16:15) *
А разве это не одно и тоже?

Не совсем понятно. Если умножению, например, требуется 5 тактов, то соответствующая команда и будет выполняться необходимое кол-во тактов и только после после этого перейдёт к выполнению следующей. Или переход к выполнению следующей команды осуществляется сразу, а результат умножения будет готов только через 5 тактов?


Навроде того.
Как захотите так и будет. Вставите 4 NOP дождётесь правильного результата.
Не вставите - никто Вам мешать не станет, процессор не станет ждать.
Но результат будет неверный. Ваше личное дело.
Програмировать открытый конвейер - это как оперировать на открытом мозге

Выход кодогенератора - формально тоже ассемблерный код, но не сильно осмысленый
Осмысленный ассемблерный код пишется программистом
Edmundo
Цитата(Degun @ Oct 23 2007, 16:15) *
Не совсем понятно. Если умножению, например, требуется 5 тактов, то соответствующая команда и будет выполняться необходимое кол-во тактов и только после после этого перейдёт к выполнению следующей. Или переход к выполнению следующей команды осуществляется сразу, а результат умножения будет готов только через 5 тактов?

Вы читали про ядро, про 8 "вычислителей", про кросс-пути и про параллельное выполнение?
И еще -- если поиграться оптимизацией -- можно получить неплохой код, не обязательно бросаться писать на АСМе. Ну да C vs. ASM -- это из другой оперы biggrin.gif
Degun
А вообще можете посоветовать:
1. каких правил или приёмов необходимо придерживаться при написании C++ кода для DSP процессора (DM642), чтобы он имел максимальную производительность.
2. что лишнего можно выключить в DSP-BIOS, чтобы уменьшить вычислительную нагрузку на процессор.
Degun
Что можно сделать или оптимизировать в функции ниже в плане генерации оптимального кода для DSP-процессора?
Код
int SplitImage2Areas(unsigned char **Image, int **AreasNumb, int *Areas2Mode, int Width, int Height, int Thres1, int Thres2)
{
    if ((Image==0)||(Width<=0)||(Height<=0)||(AreasNumb==0)) return -1;

    int iPrevAreaNumbL=-1;  //Номер области предыдущей левой точки
    int iPrevModeNumbL=-1;  //Номер моды предыдущей левой точки
    int iPrevAreaNumbU=-1;  //Номер области предыдущей верхней точки
    int iPrevModeNumbU=-1;  //Номер моды предыдущей верхней точки
    int iPrevAreaNumbUL=-1; //Номер области предыдущей верхней левой точки
    int iPrevModeNumbUL=-1; //Номер моды предыдущей верхней левой точки
    int iPrevAreaNumbUR=-1; //Номер области предыдущей верхней правой точки
    int iPrevModeNumbUR=-1; //Номер моды предыдущей верхней правой точки
    int iAreasCount=0;      //Счётчик кол-ва областей
    int i, j, y, x, iNumb[4], iNumbCount, iDelta, iTmp;
    int iCurrPixel, iCurrAreaNumb, iCurrModeNumb;
    //Изображение последовательно просматривается по строкам
    //для отнесения каждого пиксела к определённой области
    for (y=0; y<Height; y++) for (x=0; x<Width; x++)
    {
        //Значение яркости текущего пикселя
        iCurrPixel=Image[y][x];
        //Номер области текущей точки
        iCurrAreaNumb=-1;
        //Номер моды текущей точки
        iCurrModeNumb=(iCurrPixel<Thres1?0:(iCurrPixel>Thres2?1:-1));
        //Определение параметров верхней правой точки
        if ((y>0)&&(x<(Width-1)))
        {
            iPrevAreaNumbUR=AreasNumb[y-1][x+1];
            iPrevModeNumbUR=Areas2Mode[iPrevAreaNumbUR];
        }
        //В зависимости от того является ли точка переходной
        if (iCurrModeNumb>=0)
        {
            //Эта точка не является переходной ни к одной из мод
            //Просмотр всех соседних уже размеченных ранее точек
            //для возможного определения номера области текущей точки
            if ((x>0)&&(iCurrModeNumb==iPrevModeNumbL)) {
                iCurrAreaNumb=iPrevAreaNumbL; //Левая точка
            }
            else if (y>0) { //Верхняя строка
                if (iCurrModeNumb==iPrevModeNumbU) {
                    iCurrAreaNumb=iPrevAreaNumbU; //Верхняя точка
                }
                else if ((x>0)&&(iCurrModeNumb==iPrevModeNumbUL)) {
                    iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка
                }
                else if ((x<(Width-1))&&(iCurrModeNumb==iPrevModeNumbUR)) {
                    iCurrAreaNumb=iPrevAreaNumbUR; //Верхняя правая точка
                }
            }
        }
        else
        {
            //Текущая точка является переходной к любой области
            //Выбор наиболее близкой области
            iDelta=iTmp=-1;
            if (x>0) { //Левая точка
                iCurrAreaNumb=iPrevAreaNumbL;
                iDelta=abs(Image[y][x-1]-iCurrPixel);
            }
            if (y>0) { //Верхняя строка
                //Верхняя точка
                iTmp=abs(Image[y-1][x]-iCurrPixel);
                if ((iDelta<0)||(iDelta>iTmp)) {
                    iCurrAreaNumb=iPrevAreaNumbU;
                    iDelta=iTmp;
                }
                if (x>0) { //Верхняя левая точка
                    iTmp=abs(Image[y-1][x-1]-iCurrPixel);
                    if (iDelta>iTmp) {
                        iCurrAreaNumb=iPrevAreaNumbUL;
                        iDelta=iTmp;
                    }
                }
                if (x<(Width-1)) { //Верхняя правая точка
                    iTmp=abs(Image[y-1][x+1]-iCurrPixel);
                    if (iDelta>iTmp) {
                        iCurrAreaNumb=iPrevAreaNumbUR;
                        iDelta=iTmp;
                    }
                }
            }
            //Проверка на верхнюю левую точку всего изображения
            if (iCurrAreaNumb>=0)
                iCurrModeNumb=Areas2Mode[iCurrAreaNumb];
            else
                //Нужно добавить новую область с нулевой модой
                iCurrModeNumb=0;
        }
        //Если это новая область
        if (iCurrAreaNumb<0)
        {
            //Добавление нового объекта в коллекцию
            iCurrAreaNumb=iAreasCount++;
            Areas2Mode[iCurrAreaNumb]=iCurrModeNumb;
        }
        //Установка номера области текущей точки
        AreasNumb[y][x] = iCurrAreaNumb;
        //Сканирование окрестных точек для выяснения граничащих областей
        iNumbCount=0;
        if ((x==0)||(iPrevAreaNumbL!=iCurrAreaNumb))
        {
            //Просмотр левой точки
            if ((x>0)&&(iCurrModeNumb==iPrevModeNumbL)&&(iCurrAreaNumb!=iPrevAreaNumbL))
                iNumb[iNumbCount++]=iPrevAreaNumbL;
            //Просмотр верхней левой точки
            if ((y>0)&&(x>0)&&(iCurrModeNumb==iPrevModeNumbUL)&&(iCurrAreaNumb!=iPrevAreaNumbUL))
                iNumb[iNumbCount++]=iPrevAreaNumbUL;
            //Просмотр верхней точки
            if ((y>0)&&(iCurrModeNumb==iPrevModeNumbU)&&(iCurrAreaNumb!=iPrevAreaNumbU))
                iNumb[iNumbCount++]=iPrevAreaNumbU;
        }
        //Сканирование верхней правой точки (в любом случае независимо от iPrevAreaNumb!!!)
        if ((y>0)&&(x<(Width-1))&&(iCurrModeNumb==iPrevModeNumbUR)&&(iCurrAreaNumb!=iPrevAreaNumbUR))
            iNumb[iNumbCount++]=iPrevAreaNumbUR;
        //Добавление найденных граничных областей
        for (i=0; i<iNumbCount; i++)
        {
            iTmp=iNumb[i];
            bool bFind=false;
            for (j=0; j<i; j++) if (iNumb[j]==iTmp) {
                bFind=true;
                break;
            }
            if (!bFind) {
                //Здесь области добавляются к списку соседних областей
            }
        }
        //Запоминание номера области и моды для левой точки
        iPrevAreaNumbL=iCurrAreaNumb;
        iPrevModeNumbL=iCurrModeNumb;
        //Запоминание номера области и моды для верхней левой точки
        iPrevAreaNumbUL=iPrevAreaNumbU;
        iPrevModeNumbUL=iPrevModeNumbU;
        //Запоминание номера области и моды для верхней точки
        iPrevAreaNumbU=iPrevAreaNumbUR;
        iPrevModeNumbU=iPrevModeNumbUR;
    }

    return iAreasCount;
}
fontp
Неприятный какой алгоритм, сплошные условные операторы, а процессор этого толком и не умеет.

Везде где возможно избавляйтесь от if заменяя их по-возможности на MAX и MIN. Т.е. определяя границы циклов вне циклов по возможности. Да и в циклах минимизируя количество условных операций. Условные операции - смерть конвейера

А вообще-то у TI есть руководство по оптимизации кода на C. Как проектировать конвейеры на С
Там много полезных советов
Degun
Как известно у TI C6000-го семейства 8 конвейеров. А вообще приведённый код функции будет выполняться на одном конвейере или на 8-ми. Т. е. будет ли компилятор загружать автоматически все конвейеры или только один и необходимо предпринимать специальные меры, чтобы загрузить остальные 7?
Edmundo
Цитата(Degun @ Oct 30 2007, 23:12) *
Как известно у TI C6000-го семейства 8 конвейеров. А вообще приведённый код функции будет выполняться на одном конвейере или на 8-ми. Т. е. будет ли компилятор загружать автоматически все конвейеры или только один и необходимо предпринимать специальные меры, чтобы загрузить остальные 7?

Куча ветвлений в виде if-ов -- не есть истинная обработка сигналов. А посему, не будет работа DSP воистину эффективна. Для этого можно и 8051 или ARM поставить. Не для этого рождены Обработчики Сигналов...
Degun
Цитата(Edmundo @ Oct 30 2007, 23:32) *
Куча ветвлений в виде if-ов -- не есть истинная обработка сигналов. А посему, не будет работа DSP воистину эффективна. Для этого можно и 8051 или ARM поставить. Не для этого рождены Обработчики Сигналов...

Если цифровую обработку сводить только к линейной фильтрации и линейным алгоритмам вообще, то такую обработку вообще с лёгкостью можно и в ПЛИС запихнуть. Но настоящая интеллектуальность алгоритмов начинает проявляться при их нелинейности, что предполагает широкое использование if-ов. Поэтому на DSP-процессоры хотелось бы возложить именно такие более сложные алгоритмы. Да и та функция, которую я привёл не настолько сложна, чтобы сильно загружать процессор: она целочисленна; в ней нет никаких математических вычислений. Я бы даже сказал так: если уж он эту функцию не может выполнять быстро, то вообще что же он тогда может.
fontp
Цитата(Degun @ Oct 31 2007, 09:29) *
Если цифровую обработку сводить только к линейной фильтрации и линейным алгоритмам вообще, то такую обработку вообще с лёгкостью можно и в ПЛИС запихнуть. Но настоящая интеллектуальность алгоритмов начинает проявляться при их нелинейности, что предполагает широкое использование if-ов. Поэтому на DSP-процессоры хотелось бы возложить именно такие более сложные алгоритмы. Да и та функция, которую я привёл не настолько сложна, чтобы сильно загружать процессор: она целочисленна; в ней нет никаких математических вычислений. Я бы даже сказал так: если уж он эту функцию не может выполнять быстро, то вообще что же он тогда может.



Ну, он там может всякую фильтрацию, всякие преобразоввания, интерполяцию, экстраполяцию.
Если алгоритм состоит из одних if-ов то вам нужен процессор без конвейера.
Кроме того сигнальный или мультимедийный алгоритм часто можно перепроектировать (не переписать) так, что if-ов не останется, или почти не останется. Например, у DSP для нахождения экстремумов есть специальные ассемблерные инструкции и на них надо выходить или программируя на ассемблере, или с помощью интринсиков. Есть и другие хитроумные специализированые инструкции о которых стандартный С ничего не знает. Но есть интринсики ассемблерные библиотечные функции? котрые их пользуют.

Все современные DSP имеют хороший длинный конвейер и алгоритмы для них должны состоять из коротких циклов с не более одного if внутри, чтобы выполняться эффективно. А в более старых DSP не было этой напасти, зато в них было так мало регистров, что всё приходилось программировать на ассемблере, на С ничего не писалось

Понятно, что это трудно объяснить своему менеджеру, который купил Вам самый дорогой процессор (10 млрд. МИПС :-)) на все случаи жизни. Менеджер - это ведь тот человек, который понятия не имеет, где он находится, но совершенно точно знает, кто виноват :-)

По поводу Вашего предыдущего вопроса - сам он ничего не будет загружать. Компилятор будет делать попытки, но ... оптимизирующий компилятор должен узнать один из известных ему шаблонов, чтобы сгенерировать эффективный код. C FIR фильтром он разберётся лучше программиста, а так не очень...
Что ему программист скажет загрузить (с помощью ассемблера или интринсиков) - то он примерно и загрузит
Degun
Цитата(fontp @ Oct 31 2007, 11:42) *
Ну, он там может всякую фильтрацию, всякие преобразоввания, интерполяцию, экстраполяцию.
Если алгоритм состоит из одних if-ов то вам нужен процессор без конвейера.

Конечно не из одних, но их достаточное кол-во. Типовые задачи цифровой обработки сигналов также присутствуют. Вообще с трудом представляю себе алгоритмы без if-ов. Например, сравнение одной переменной с другой и в зависимости от результата ветвление - это if. А уж без этого трудно представить большинство алгоритмов.
Цитата(fontp @ Oct 31 2007, 11:42) *
Понятно, что это трудно объяснить своему менеджеру, который купил Вам самый дорогой процессор (10 млрд. МИПС :-)) на все случаи жизни.

На самом деле производительность всего 4800 МИПС. Теоретически. Но чтобы их вытянуть у процессора на языке C\C++ нужно приложить массу усилий или перейти на ассемблер. Причём тип процессора был выбран вполне обосновано и претензий к этому у меня нет. Как показывает практика такой производительности процессора не хватает для обработки кадров с разрешением 768*576 с частотой 50 Гц по приведённому выше алгоритму, т. к. эта функция для указанного разрешения выполняется порядка 1 сек!
Цитата(fontp @ Oct 31 2007, 11:42) *
По поводу Вашего предыдущего вопроса - сам он ничего не будет загружать. Компилятор будет делать попытки, но ... оптимизирующий компилятор должен узнать один из известных ему шаблонов, чтобы сгенерировать эффективный код. C FIR фильтром он разберётся лучше программиста, а так не очень...
Что ему программист скажет загрузить (с помощью ассемблера или интринсиков) - то он примерно и загрузит

А есть ли документ где можно посмотреть типовые шаблоны алгоритмов?
fontp
Цитата(Degun @ Oct 31 2007, 22:50) *
А есть ли документ где можно посмотреть типовые шаблоны алгоритмов?


SPRU187 - TMS320C6000 Optimizing Compiler User's Guide
SPRU198- TMS320C6000 Programmer's Guide
SPRU565-TMS320C64x DSP Library Programmer's Reference
SPRU023-TMS320C64x Image/Video Processing Library Programmer’s Reference
и т.д.

они все есть у Вас в 3-м композере
C:\CCStudio_v3.1\docs\pdf\manuals_ccs_full_c6000.html

В первой из книг рассказывается как поменять мозги и программировать в стиле шестьдесятого TMS
конвейеры. Это действительно в каком-то обобщённом смысле больше похоже на проектирование конвейеров ПЛИС , а не обычное последовательное программирование

Другие - описания библиотек. Есть же и исходники если интересно
Приёмчики, которыми пользуется оптимизатор, вряд ли где-то описаны. С опытом почувствуете

Между прочим pentium-мы на чисто логических задачах - тоже страшный тормоз, например на шахматных программах. И причины те же. Это всё процессоры заточеные под потоковые задачи

Короче, нужно или перепроектировать алгоритм (для вычислительных алгоритмов это возможно почти всегда), либо если задача типа перебора на дереве решений с нелинейными метриками - не рассчитывать на
быстродействие 500<мгц>х8<число конвейеров> мипс, а рассчитывать скорее на быстродействие

500мгц/ <глубина конвейера>. В вашем случае 50-100 мипс. Последний факт, понятно, аккуратно умалчивается не только в рекламных листовках, но и в документации на процессор. Харизма не та, чтобы прямо сказать: "Вот такие мы, Тексас (Аналог, Интел), мудАки" :-)
Degun
Цитата(fontp @ Oct 31 2007, 23:20) *
Короче, нужно или перепроектировать алгоритм (для вычислительных алгоритмов это возможно почти всегда), либо если задача типа перебора на дереве решений с нелинейными метриками - не рассчитывать на
быстродействие 500<мгц>х8<число конвейеров> мипс, а рассчитывать скорее на быстродействие

500мгц/ <глубина конвейера>. В вашем случае 50-100 мипс. Последний факт, понятно, аккуратно умалчивается не только в рекламных листовках, но и в документации на процессор. Харизма не та, чтобы прямо сказать: "Вот такие мы, Тексас (Аналог, Интел), мудАки" :-)

Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера?
fontp
Цитата(Degun @ Nov 7 2007, 22:59) *
Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера?




Это количество конвейеров. Если постараться и повезёт будут выполняться 8 инструкций.

Если не стараться или не повезёт, то конвейер (ы) не будет загружен и будет простаивать.

У конвейера есть две временных характеристики - bandwidth и latency. Если удаётся подпихивать конвейеру данные на тактовой частоте - то быстродействие равно bandwith. Если не удаётся то можно заработать 1/latency

Загрузить конвейер - это искусство, если задача не стандартная. И уж совершенно точно компилятор этим искусством не владеет
qxov
Цитата(Degun @ Oct 26 2007, 16:06) *
Что можно сделать или оптимизировать в функции ниже в плане генерации оптимального кода для DSP-процессора?
Код
int SplitImage2Areas(unsigned char **Image, int **AreasNumb, int *Areas2Mode, int Width, int Height, int Thres1, int Thres2)
[поудалял // qxov]


Я хотел бы попытался подсказать, как можно улучшить этот код, но для этого желательно на словах объяснить, что он должен делать - код не прозрачен и практически не читается, к сожалению. Подскажите, если задача не потеряла актуальность.

Цитата(Edmundo @ Oct 24 2007, 16:36) *
Вы читали про ядро, про 8 "вычислителей", про кросс-пути и про параллельное выполнение?
И еще -- если поиграться оптимизацией -- можно получить неплохой код, не обязательно бросаться писать на АСМе. Ну да C vs. ASM -- это из другой оперы biggrin.gif

На начальном этапе оптимизации многое из этого излишне. Скажем, кросс-пути - это вообще в последнюю очередь должно волновать голову, полно гораздо более значимых моментов.

Цитата(Degun @ Oct 31 2007, 00:12) *
Как известно у TI C6000-го семейства 8 конвейеров. А вообще приведённый код функции будет выполняться на одном конвейере или на 8-ми. Т. е. будет ли компилятор загружать автоматически все конвейеры или только один и необходимо предпринимать специальные меры, чтобы загрузить остальные 7?

У этого семейства 8 юнитов, по 2 каждого типа: .L, .S, .D, .M.
В частности, в SPRU732C Appendix B есть таблица "Mapping Between Instruction and Functional Unit".
Например, в .M юнитах выполняются, в основном, умножения, в .L - логические операции и т.д. То есть, нельзя загрузить все юниты, если не используются соответствующие инструкции. Компилятор очень хорошо оптимизирует код, но только в том случае, если ему предоставить такую возможность - дать знать, сколько раз (обычно) выполняются циклы, вытащить из циклов вызовы функций и т.д. Большое количество разных if - смерть для оптимизатора.
Стоит заметить, что у этих процессоров все инструкции являются условными, что можно грамотно применить при написании кода, получив ускорение даже в несколько раз, по сравнению с написанием "в лоб", при этом не приходится даже серьезно модифицировать алгоритм.
Degun
Цитата(qxov @ Nov 16 2007, 13:07) *
Я хотел бы попытался подсказать, как можно улучшить этот код, но для этого желательно на словах объяснить, что он должен делать - код не прозрачен и практически не читается, к сожалению. Подскажите, если задача не потеряла актуальность.

Задача состоит в следующем: на вход функции поступает полутоновое (серое) 8-ми битовое изображение Image размерами Height и Width. Типичный пример изображения - звёзды на фоне космического пространства. Необходимо разбить изображение на непересекающиеся пронумерованные области в соответствии с двуми порогами: Thr1 - порог фона (нижний порог); Thr2 - порог объектов (верхний порог). Те пикселы, которые находятся между порогами Thr1 и Thr2 (т. е. Thr1 < PixelValue < Thr2) являются переходными и могут быть отнесены к любой ближайшей наиболее близкой (с точки зрения близости значений пикселов) области. На выходе функции в массиве AreasNumbBuf для каждого пиксела сохраняется номер области, к которой принадлежит этот пиксель (это может быть или космическое пространство или звезда или НЛОsmile.gif).
Алгоритм с последней публикации был достаточно сильно изменён, поэтому привожу его заново. Но несмотря на значительную оптимизацию его быстродействие на процессоре DM642 оставляет желать лучшего. Например для изображения размерами 640*480 он выполняется порядка 0.6 сек, что неприемлемо. Планировалось, что он должен выполняться порядка 20 мсек (50 Гц).
Код
#include <stdlib.h>

//Максимальное кол-во областей в изображении
#define MAX_AREAS 1000
#define min(x,y) (x<y?x:y)

//Структура хранения информации об областях
typedef struct TAreaInfo
{
    //Номер кластера
    short iNumber;
    //Номер моды
    short iMode;
    //Счётчик мощности моды
    int iCount;
} TAreaInfo;

//Счётчики количества областей в изображении и в контейнере
static int iAreasCount=0, iAreasInfoCount=0;

//Ограниченный массив структур описателей областей!!!
static TAreaInfo AreasInfo[MAX_AREAS+1];

//Таблица соответствия значений пикселов модам гистограммы
static int ModeNumb4Pix[256];

//Функция создания новой области
static inline int AddNewAreaInfo(const int iModeNumb)
{
//    if (iAreasInfoCount>=MAX_AREAS) return -1;
    iAreasCount++;
    AreasInfo[iAreasInfoCount].iMode=iModeNumb;
    AreasInfo[iAreasInfoCount].iNumber=-1;
    AreasInfo[iAreasInfoCount].iCount=0;
    return iAreasInfoCount++;
}

/*!\brief Разбиение изображения на непересекающиеся области
* \param Image - исходное изображение
* \param Height - высота изображения
* \param Width - ширина изображение
* \param AreasNumbBuf - буфер результата, отражающий номера областей каждого из пикселов
* \param Thr1 - нижний порог
* \param Thr2 - верхний порог
* \result Количество непересекающихся областей в изображении
*/
int SplitImage2Areas(
    register unsigned char ** __restrict Image,
    register int Height, register int Width,
    register int ** __restrict AreasNumbBuf,
    register int Thr1, register int Thr2)
{
    //Изображение последовательно просматривается по строкам
    //для отнесения каждого пиксела к определённой области
    //Вычисление площади, занимаемой каждой областью и модой
    register int iPrevAreaNumbL=-1;  //Номер области предыдущей левой точки
    register int iPrevModeNumbL=-1;  //Номер моды предыдущей левой точки
    register int iPrevAreaNumbU=-1;  //Номер области предыдущей верхней точки
    register int iPrevModeNumbU=-1;  //Номер моды предыдущей верхней точки
    register int iPrevAreaNumbUL=-1; //Номер области предыдущей верхней левой точки
    register int iPrevModeNumbUL=-1; //Номер моды предыдущей верхней левой точки
    register int iPrevAreaNumbUR=-1; //Номер области предыдущей верхней правой точки
    register int iPrevModeNumbUR=-1; //Номер моды предыдущей верхней правой точки
    //Временные переменные
    register int i, y, x, iSrcIndex, iDstIndex, iDelta, iTmp;
    register int iCurrPixel, iCurrAreaNumb, iCurrModeNumb;
    register TAreaInfo * __restrict pSrcObj, * __restrict pDstObj, * __restrict pTmpObj;

    //Проверка корректности входных параметров
    if ((Image==0)||(Width<=0)||(Height<=0)||(ModeNumb4Pix==0)||(AreasNumbBuf==0)) return -1;
    //Формирование таблицы мод пикселей
    for (i=0; i<256; i++)
    {
        if (i<Thr1)
            ModeNumb4Pix[i]=0;
        else if (i>=Thr2)
            ModeNumb4Pix[i]=1;
        else
            ModeNumb4Pix[i]=-1;
    }
    //Обнуление счётчиков кол-ва областей
    iAreasCount=iAreasInfoCount=0;
    //Обработка верхней левой точки изображения
    iCurrPixel=Image[0][0];
    iCurrModeNumb=ModeNumb4Pix[iCurrPixel];
    if (iCurrModeNumb<0)
    {
        //Сканирование зигзагом окрестных точек для
        //выявления ближайшей с установленной модой
        iDelta=min(Width,Height);
        for (i=1; i<iDelta; i++)
        {
            iSrcIndex=-1;
            for (y=i, x=0; y>=0; y--, x++)
            {
                iDstIndex=Image[y][x];
                iTmp=ModeNumb4Pix[iDstIndex];
                if (iTmp<0) continue;
                iDstIndex=abs(iDstIndex-iCurrPixel);
                if ((iSrcIndex<0)||(iSrcIndex>iDstIndex))
                {
                    iSrcIndex=iDstIndex;
                    iCurrModeNumb=iTmp;
                }
            }
            //Нашли подходящую точку - выход из поиска
            if (iCurrModeNumb>=0) break;
        }
        //Если точек нет, то нулевая мода
        if (iCurrModeNumb<0) iCurrModeNumb=0;
    }
    iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
    AreasNumbBuf[0][0]=iCurrAreaNumb;
    //Обработка первой верхней строки изображения,
    //начиная со второго пиксела строки
    for (x=1; x<Width; x++)
    {
        //Запоминание номера области и моды для левой точки
        iPrevAreaNumbL=iCurrAreaNumb;
        iPrevModeNumbL=iCurrModeNumb;
        //Номер моды текущей точки
        iCurrModeNumb=ModeNumb4Pix[Image[0][x]];
        //В зависимости от наличия моды
        if (iCurrModeNumb<0) {
            iCurrModeNumb=iPrevModeNumbL;
            iCurrAreaNumb=iPrevAreaNumbL;
        }
        else if (iCurrModeNumb==iPrevModeNumbL) {
            iCurrAreaNumb=iPrevAreaNumbL;
        }
        else {
            //Добавление новой области
            iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
        }
        //Установка номера области текущей точки
        AreasNumbBuf[0][x] = iCurrAreaNumb;
        //Инкремент счётчика площади, занимаемой областью
        AreasInfo[iCurrAreaNumb].iCount++;
    }
    //Декремент ширины изображения для увеличения скорости обработки
    Width--;
    //Цикл по всем точкам изображения
    for (y=1; y<Height; y++)
    {
        //Установка начального значения для верхней левой точки
        iPrevAreaNumbUL=AreasNumbBuf[y-1][0];
        iPrevModeNumbUL=AreasInfo[iPrevAreaNumbUL].iMode;
        //Установка начального значения для верхней точки
        iPrevAreaNumbU=AreasNumbBuf[y-1][1];
        iPrevModeNumbU=AreasInfo[iPrevAreaNumbU].iMode;
        //Значение яркости первого пикселя строки
        iCurrPixel=Image[y][0];
        //Значение моды первого пикселя строки
        iPrevModeNumbL=ModeNumb4Pix[iCurrPixel];
        //В зависимости от признака переходности пиксела
        if (iPrevModeNumbL<0) {
            if (abs(iCurrPixel-Image[y-1][0])<abs(iCurrPixel-Image[y-1][1]))
            {
                iPrevModeNumbL=iPrevModeNumbUL;
                iPrevAreaNumbL=iPrevAreaNumbUL;
            }
            else
            {
                iPrevModeNumbL=iPrevModeNumbU;
                iPrevAreaNumbL=iPrevAreaNumbU;
            }
        }
        else if (iPrevModeNumbL==iPrevModeNumbUL)
            iPrevAreaNumbL=iPrevAreaNumbUL; //Верхняя левая точка
        else if (iPrevModeNumbL==iPrevModeNumbU)
            iPrevAreaNumbL=iPrevAreaNumbU;  //Верхняя точка
        else {
            //Добавление новой области
            iPrevAreaNumbL=AddNewAreaInfo(iPrevModeNumbL);
        }
        //Установка номера области первого пиксела строки
        AreasNumbBuf[y][0] = iPrevAreaNumbL;
        //Инкремент счётчика площади, занимаемой областью
        AreasInfo[iPrevAreaNumbL].iCount++;
        //Обработка очередной строки изображения
        for (x=1; x<Width; x++)
        {
            //Определение параметров верхней правой точки
            iPrevAreaNumbUR=AreasNumbBuf[y-1][x+1];
            iPrevModeNumbUR=AreasInfo[iPrevAreaNumbUR].iMode;
            //Значение яркости текущего пикселя
            iCurrPixel=Image[y][x];
            //Номер моды текущей точки
            iCurrModeNumb=ModeNumb4Pix[iCurrPixel];
            //В зависимости от того является ли точка переходной
            if (iCurrModeNumb<0)
            {
                //Текущая точка является переходной к любой области
                //Перебор всех точек окрестности и выбор наиболее близкой области
                //Левая точка
                iDelta=abs(Image[y][x-1]-iCurrPixel);
                iCurrAreaNumb=iPrevAreaNumbL;
                iCurrModeNumb=iPrevModeNumbL;
                //Верхняя точка
                iTmp=abs(Image[y-1][x]-iCurrPixel);
                if (iDelta>iTmp) {
                    iDelta=iTmp;
                    iCurrAreaNumb=iPrevAreaNumbU;
                    iCurrModeNumb=iPrevModeNumbU;
                }
                //Верхняя левая точка
                iTmp=abs(Image[y-1][x-1]-iCurrPixel);
                if (iDelta>iTmp) {
                    iDelta=iTmp;
                    iCurrAreaNumb=iPrevAreaNumbUL;
                    iCurrModeNumb=iPrevModeNumbUL;
                }
                //Верхняя правая точка
                iTmp=abs(Image[y-1][x+1]-iCurrPixel);
                if (iDelta>iTmp) {
                    iDelta=iTmp;
                    iCurrAreaNumb=iPrevAreaNumbUR;
                    iCurrModeNumb=iPrevModeNumbUR;
                }
            }
            //Эта точка не является переходной ни к одной из мод
            //Просмотр всех соседних уже размеченных ранее точек
            //для возможного определения номера области текущей точки
            else if (iCurrModeNumb==iPrevModeNumbL)
                    iCurrAreaNumb=iPrevAreaNumbL;  //Левая точка
            else if (iCurrModeNumb==iPrevModeNumbUL)
                    iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка
            else if (iCurrModeNumb==iPrevModeNumbU)
                    iCurrAreaNumb=iPrevAreaNumbU;  //Верхняя точка
            else if (iCurrModeNumb==iPrevModeNumbUR)
                    iCurrAreaNumb=iPrevAreaNumbUR; //Верхняя правая точка
            else {
                //Добавление новой области
                iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
            }
            //Установка номера области текущей точки
            AreasNumbBuf[y][x] = iCurrAreaNumb;
            //Объект области в дереве
            pSrcObj=&AreasInfo[iCurrAreaNumb];
            //Инкремент счётчика площади, занимаемой областью
            pSrcObj->iCount++;
            //Сканирование верхней правой точки для добавления её в список соседних областей
            if ((iCurrModeNumb==iPrevModeNumbUR)&&(iCurrAreaNumb!=iPrevAreaNumbUR))
            {
                //Объект области предыдущей ссылки
                pDstObj=&AreasInfo[iPrevAreaNumbUR];
                //Всего четыре возможных варианта
                if (pSrcObj->iNumber<0)
                {
                    if (pDstObj->iNumber>=0)
                    {
                        //Только добавляется ссылка на ссылочную область
                        pSrcObj->iNumber=pDstObj->iNumber;
                        //Декремент кол-ва областей
                        iAreasCount--;
                    }
                    else
                    {
                        //Обе области являются доменными
                        iTmp=min(iCurrAreaNumb,iPrevAreaNumbUR);
                        pSrcObj->iNumber=iTmp;
                        pDstObj->iNumber=iTmp;
                        //Декремент кол-ва областей
                        iAreasCount--;
                    }
                }
                else //if (pSrcObj->iNumber>=0)
                {
                    if (pDstObj->iNumber<0)
                    {
                        //Только добавляется ссылка на ссылочную область
                        pDstObj->iNumber=pSrcObj->iNumber;
                        //Декремент кол-ва областей
                        iAreasCount--;
                    }
                    else
                    {
                        iSrcIndex=pSrcObj->iNumber;
                        iDstIndex=pDstObj->iNumber;
                        if (iSrcIndex!=iDstIndex)
                        {
                            //Это самый трудоёмкий случай.
                            //Но, к счастью, самый редкий!
                            //Необходимо объединить две области
                            for (i=0; i<iAreasInfoCount; i++)
                            {
                                pTmpObj=&AreasInfo[i];
                                if (pTmpObj->iNumber==iSrcIndex)
                                    pTmpObj->iNumber=iDstIndex;
                            }
                            //Декремент кол-ва областей
                            iAreasCount--;
                        }
                    }
                }
            }
            //Запоминание номера области и моды для левой точки
            iPrevAreaNumbL=iCurrAreaNumb;
            iPrevModeNumbL=iCurrModeNumb;
            //Запоминание номера области и моды для верхней левой точки
            iPrevAreaNumbUL=iPrevAreaNumbU;
            iPrevModeNumbUL=iPrevModeNumbU;
            //Запоминание номера области и моды для верхней точки
            iPrevAreaNumbU=iPrevAreaNumbUR;
            iPrevModeNumbU=iPrevModeNumbUR;
        }
        //Значение яркости последнего пикселя строки
        iCurrPixel=Image[y][Width];
        //Значение моды последнего пикселя строки
        iCurrModeNumb=ModeNumb4Pix[iCurrPixel];
        //В зависимости от наличия моды
        if (iCurrModeNumb<0) {
            //Левая точка
            iDelta=abs(Image[y][x-1]-iCurrPixel);
            iCurrAreaNumb=iPrevAreaNumbL;
            iCurrModeNumb=iPrevModeNumbL;
            //Верхняя левая точка
            iTmp=abs(Image[y-1][x-1]-iCurrPixel);
            if (iDelta>iTmp) {
                iDelta=iTmp;
                iCurrAreaNumb=iPrevAreaNumbUL;
                iCurrModeNumb=iPrevModeNumbUL;
            }
            //Верхняя точка
            iTmp=abs(Image[y-1][x]-iCurrPixel);
            if (iDelta>iTmp) {
                iDelta=iTmp;
                iCurrAreaNumb=iPrevAreaNumbU;
                iCurrModeNumb=iPrevModeNumbU;
            }
        }
        else if (iCurrModeNumb==iPrevModeNumbL)
            iCurrAreaNumb=iPrevAreaNumbL;  //Левая точка
        else if (iCurrModeNumb==iPrevModeNumbUL)
            iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка
        else if (iCurrModeNumb==iPrevModeNumbU)
            iCurrAreaNumb=iPrevAreaNumbU;  //Верхняя правая точка
        else {
            //Добавление новой области
            iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
        }
        //Установка номера области последнего пикселя строки
        AreasNumbBuf[y][Width] = iCurrAreaNumb;
        //Инкремент счётчика площади, занимаемой областью
        AreasInfo[iCurrAreaNumb].iCount++;
    }
    //Восстановление исходной ширины изображения
    Width++;

    //Упорядочивание переадресации областей (именно здесь)!!!!!
    for (i=0; i<iAreasInfoCount; i++)
    {
        pTmpObj=&AreasInfo[i];
        iTmp=pTmpObj->iNumber;
        if (iTmp<0)
        {
            //Это независимый кластер
            pTmpObj->iNumber=i;
        }
        else if (iTmp!=i)
        {
            //Это не кластерная область
            AreasInfo[iTmp].iCount+=pTmpObj->iCount;
            //Обнуление счётчика - выключение области
            pTmpObj->iCount=0;
        }
    }

    //Могут оставаться независимые области с индексами больше iAreasCount
    for (iDstIndex=0, iSrcIndex=iAreasCount; iSrcIndex<iAreasInfoCount; iSrcIndex++)
    {
        //Если эта область уже переадресована - все нормально
        pSrcObj=&AreasInfo[iSrcIndex];
        if ((pSrcObj==NULL)||(pSrcObj->iCount==0)) continue;
        //Поиск индекса для переадресации
        for (; iDstIndex<iAreasCount; iDstIndex++)
        {
            pDstObj=&AreasInfo[iDstIndex];
            if ((pDstObj!=NULL)&&(pDstObj->iCount==0)) break;
        }
        //Сохранение указателя на моду
        pDstObj->iMode=pSrcObj->iMode;
        //Коррекция счётчиков мощности областей
        pDstObj->iCount=pSrcObj->iCount;
        pSrcObj->iCount=0;
        //Установка ссылки данной области на переадресованную
        pSrcObj->iNumber=iDstIndex;
        //Замена всех ссылок на эту область!!!!!!!!
        for (i=0; i<iAreasInfoCount; i++)
        {
            pTmpObj=&AreasInfo[i];
            if (pTmpObj->iNumber==iSrcIndex) pTmpObj->iNumber=iDstIndex;
        }
    }

    //Переписывание индексов областей на новые переназначенные
    for (y=0; y<Height; y++) for (x=0; x<Width; x++)
    {
        iTmp=AreasNumbBuf[y][x];
        iTmp=AreasInfo[iTmp].iNumber;
        AreasNumbBuf[y][x]=iTmp;
    }

    return iAreasCount;
}
qxov
Цитата(Degun @ Nov 16 2007, 16:08) *
Задача состоит в следующем: на вход функции поступает полутоновое (серое) 8-ми битовое изображение Image размерами Height и Width. Типичный пример изображения - звёзды на фоне космического пространства. Необходимо разбить изображение на непересекающиеся пронумерованные области в соответствии с двуми порогами: Thr1 - порог фона (нижний порог); Thr2 - порог объектов (верхний порог). Те пикселы, которые находятся между порогами Thr1 и Thr2 (т. е. Thr1 < PixelValue < Thr2) являются переходными и могут быть отнесены к любой ближайшей наиболее близкой (с точки зрения близости значений пикселов) области. На выходе функции в массиве AreasNumbBuf для каждого пиксела сохраняется номер области, к которой принадлежит этот пиксель (это может быть или космическое пространство или звезда или НЛОsmile.gif).
Алгоритм с последней публикации был достаточно сильно изменён, поэтому привожу его заново. Но несмотря на значительную оптимизацию его быстродействие на процессоре DM642 оставляет желать лучшего. Например для изображения размерами 640*480 он выполняется порядка 0.6 сек, что неприемлемо. Планировалось, что он должен выполняться порядка 20 мсек (50 Гц).


Вот плохо понятно, как, всё-таки, производится разбиение на области smile.gif
Вот так можно загружать процессор по полной программе (сам не тестировал, ибо дома композера нет):

Код
void calcModes(unsigned char * restrict image, char * restrict modes, int pixelsCount, int thresold1, int thresold2)
{  
    int thresoldLo=_packl4(_pack2(thresold1,thresold1),_pack2(thresold1,thresold1));
    int thresoldHi=_packl4(_pack2(thresold2,thresold2),_pack2(thresold2,thresold2));

    for(int i=0;i<pixelsCount;i+=8)
    {
        // Загрузим сразу 8 пикселов
        double pixels=_amemd8_const(image+i);                       // D

        // Если пиксел >= thresold1, то в соответсвующем байте будет 0xff
        int compare1Lo=_xpnd4(_cmpgtu4(_lo(pixels),thresoldLo));    // L + M

        // Если пиксел >= thresold2, то в соответсвующем байте будет 0xff
        int compare1Hi=_xpnd4(_cmpgtu4(_lo(pixels),thresoldHi));    // L + M

        // Здесь очень удачно все сложилось - возможны три варианта:
        // если пиксел>=thresold1 и пиксел<thresold2, то получится 0xff ^ 0x00 = 0xff = -1;
        // если пиксел>=thresold1 и пиксел>=thresold2, то получится 0xff ^ 0xfe = 0x01
        // если пиксел<thresold, то пиксел<thresold2 и получается 0x00 ^ 0x00 = 0x00
        int result1=compare1Lo ^ (compare1Hi & 0xfefefefe);         // LSD + LSD


        // Здесь все тоже самое, для оставшихся четырех пикселов
        int compare2Lo=_xpnd4(_cmpgtu4(_hi(pixels),thresoldLo));    // L + M
        int compare2Hi=_xpnd4(_cmpgtu4(_hi(pixels),thresoldHi));    // L + M

        int result2=compare2Lo ^ (compare2Hi & 0xfefefefe);         // LSD + LSD

        // Сохраним результат
        _amemd8(image+i)=_itod(result2,result1);                    // D

        // Здесь получаем, что 4L и 4M юнита - два цикла; еще осталось 4S и 4D, 2D на загрузку
        // уйдет -> остается 4S и 2D юнита, для внутренностей цикла надо 4LSD => останется два S
        // юнита на поддержание цикла. Теоретически, производительность кода - 2 такта на 8 пикселов
        // или 0.25 такта на пиксел. При этом удалось добиться соответствия со значениями мод из
        // оригинального кода, добавив лишь одно ограничение по - количество пикселей должно быть кратно 8.
        // Следует также отметить, что даже при больших размерах изображения, не возникает простоя из-за
        // того, что данные не находятся в кэше.
    }
}
Degun
Цитата(qxov @ Nov 17 2007, 00:53) *
Вот плохо понятно, как, всё-таки, производится разбиение на области smile.gif

Для примера представьте себе изображение, где на черном фоне имеются три белых геометрических фигуры: прямоугольник, треугольник и круг. Тогда на выходе алгоритма в массиве AreasNumbBuf будет следующий результат:

00000000000000000000000000000000000000000000
00011111111111111111000000000000000000200000
00011111111111111111000000000000000002200000
00011111111111111111000000000000000022200000
00011111111111111111000000000000000222200000
00011111111111111111000000000000002222200000
00011111111111111111000000000000022222200000
00000000000000000000000000000000222222200000
00000000000333333000000000000002222222200000
00000003333333333333300000000022222222200000
00003333333333333333333300000222222222200000
00333333333333333333333333002222222222200000
03333333333333333333333333300000000000000000
00333333333333333333333333000000000000000000
00003333333333333333333300000000000000000000
00000003333333333333300000000000000000000000
00000000000333333000000000000000000000000000
00000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000

Т. е. исходный массив значений пикселов был преобразован в массив, где вместо значений пикселов находятся номера областей, к которым относятся данные пикселы. Всего здесь 4 области.
При обработке алгоритм последовательно проходит по всем строкам изображения и каждый пиксел относит к одной из трёх возможных групп (мод):
1. группа (мода) фоновых пикселов: Pixel_Value<Threshold1
2. группа (мода) пикселов объектов: Pixel_Value>=Threshold2
3. группа (мода) переходных пикселов: Threshold1<=Pixel_Value<Threshold2, которые при работе алгоритма могут быть в итоге отнесены как к первой, так и ко второй группе (моде).
На основе принадлежности текущего пиксела к той или иной группе (моде) делается вывод о том принадлежит ли текущий пиксел к уже размеченной ранее области или этот пиксел принадлежит к новой области. Для этого у текущего пиксела просматриваются четыре точки его окрестности (левая; верхняя левая; верхняя; верхняя правая точки) и если среди них найдена точка с такой же модой как у него, то номер области текущего пиксела будет такой же как у того пиксела, с которым у него совпала мода. Если таких нет, то добавляется новая область. Если пиксел относится к переходной моде, то из четырех точек его окрестности выбирается наиболее близкий по абсолютному значению яркости пиксел и берётся его номер области.
Вкраце примерно так работает алгоритм. За основным циклом происходит ещё перенос областей с номерами большими, чем общее кол-во областей. Также производится окончательная перенумерация областей.
mdmitry
Возможно оффтор! В MATLAB ImageProcessing Toolbox есть функции для фильтрации изображений, в том числе выделения областей по разным признакам. Может быть примененные там алгоритмы Вам помогут.
qxov
Цитата(Degun @ Nov 16 2007, 16:08) *
Алгоритм с последней публикации был достаточно сильно изменён, поэтому привожу его заново. Но несмотря на значительную оптимизацию его быстродействие на процессоре DM642 оставляет желать лучшего. Например для изображения размерами 640*480 он выполняется порядка 0.6 сек, что неприемлемо. Планировалось, что он должен выполняться порядка 20 мсек (50 Гц).

А вот возникает еще вопрос: 640*480*2 байт памяти для 640x480, как минимум, используется, что равно 600Кб. Если верить ti, то самое бодрое, что есть - TMS320DM642-720 c 256 KB внутренней памяти. Это получается, что во внешней памяти данные хранятся или я не понимаю всего?
Далее, 720e6/640/480/50 = 46.875. То есть, для 640x480x50 раз/сек есть, в лучшем случае, 46 тактов. Реально - еще меньше. Однозначно, работать нужно из внутренней памяти, причем не просто из внутренней памяти, а еще и так, чтобы данные укладывались в кэш первого уровня, иначе, грубо говоря, время выполнения увеличится чуть ли не в два раза.
Таким образом необходимо:
1. Создать во внутреней памяти буфера для хранения трех строк:
1.1. Текущая строка
1.2. Предыдущая строка.
1.3. Следующая строка.
2. Настроить канал ДМА, чтобы во время обработки буферов (1.2.) и (1.1.) копировались данные из внешней памяти в (1.3.).
3. Дожидаемся окончания работы ДМА, свопим буфера и запускаем снова канал ДМА, а сами тем временем обрабатываем предыдущие строки.

И, думаю, если копнуть глубже, то окажется, что времени не 1/50 секунды есть, а 1/25, не так ли? Реально частота кадров какая?

Здесь надо менять очень серьезно все. А уже потом - оптимизировать код, там тоже есть, где разгуляться.
qxov
Как успехи? Вот, набросал на коленке кусочек. Может, пригодиться...

int envPixelsCompareLo = _xpnd4(_cmpgtu4(envPixelsBrightness4,thresold1));
int envPixelsCompareHi = _xpnd4(_cmpgtu4(envPixelsBrightness4,thresold2));
int envPixelsModes4 = envPixelsCompareLo ^ (envPixelsCompareHi & 0xfefefefe);

unsigned char currPixelBrightness=currLine[i];
int currPixelBrightness4=_lo(_mpyli(currPixelBrightness,0x01010101));
int currPixelCompareLo = _xpnd4(_cmpgtu4(currPixelBrightness4,thresold1));
int currPixelCompareHi = _xpnd4(_cmpgtu4(currPixelBrightness4,thresold2));
int currPixelMode4 = currPixelCompareLo ^ (currPixelCompareHi & 0xfefefefe);

int equalModesCompare=_cmpeq4(currPixelMode4,envPixelsModes4); // 1 там, где моды совпадают

int absDeltas = _subabs4(currPixelBrightness4,envPixelsBrightness);
int minTmp = _minu4(absDeltas,_swap4(absDeltas));
int minAbs4 = _min2(minTmp, _packlh2(minTmp, minTmp));
int minAbsCompare=_cmpeq4(minAbs4, absDeltas); // 1 там, где минимальная разница в яркости

int minAbsPos=31-_norm(minAbsCompare);
int equalModesPos=31-_norm(equalModesCompare);

int areaPos=(minAbsPos<=equalModesPos) ? minAbsPos : equalModesPos;

int thisPixelArea=(areaPos) ? envAreas[areaPos-1] : lastAreaNo++;
vadkudr
Цитата(Degun @ Nov 8 2007, 04:59) *
Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера?

C4itajte, 4to eto 8 ALU, spezializirovannyh kazdyj pod svoju zada4u (umnozhenie slozhenie adresazija)
Kazdoe iz alu imeet svoj konveer. glubina ih razli4na. Umnozhiteli i summatory imejut glubinu - 4 (dlja float) Adresatory - dlinu 5. Programmirovat' eto vse, 4tob rabotalo parallelno, nado o4en' vnimatel'no (esli ru4kami na assemblere - kubik Rubika uzhe upominalsja). Ili usilenno pol'zovat'sja pragmami i optimizaziej compiljatora. 4itajte documentaziju po optimizazii ziklov (fontp privodil spisok). A compiljator k C6000 vpolne gramotno napisan. Sam videl,4to pripravilnoj rasstanovke pragm on delal 4asto kod lu46e, 4em ja ru4kami.
Degun
Цитата(qxov @ Nov 17 2007, 00:53) *
Вот так можно загружать процессор по полной программе:
Код
void calcModes(unsigned char * restrict image, char * restrict modes, int pixelsCount, int thresold1, int thresold2)
{
...  
}

Это полезная процедура. Большое спасибо. Но остаётся сложная часть алгоритма, где много if-ов, которая определяет логику создания областей, которую я не понимаю как оптимизировать. Взять хотя бы "нумеризацию" первой строки:
Код
    //Обработка первой верхней строки изображения, начиная со второго пиксела строки
    for (x=1; x<Width; x++)
    {
        //Запоминание номера области и моды для левой точки
        iPrevAreaNumbL=iCurrAreaNumb;
        iPrevModeNumbL=iCurrModeNumb;
        //Номер моды текущей точки
        iCurrModeNumb=ModeNumb4Pix[Image[0][x]];
        //В зависимости от наличия моды
        if (iCurrModeNumb<0) {
            iCurrModeNumb=iPrevModeNumbL;
            iCurrAreaNumb=iPrevAreaNumbL;
        }
        else if (iCurrModeNumb==iPrevModeNumbL) {
            iCurrAreaNumb=iPrevAreaNumbL;
        }
        else {
            //Добавление новой области
            iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
        }
        //Установка номера области текущей точки
        AreasNumbBuf[0][x] = iCurrAreaNumb;
        //Инкремент счётчика площади, занимаемой областью
        AreasInfo[iCurrAreaNumb].iCount++;
    }

Цитата(qxov @ Nov 18 2007, 16:15) *
Таким образом необходимо:
1. Создать во внутреней памяти буфера для хранения трех строк:
1.1. Текущая строка
1.2. Предыдущая строка.
1.3. Следующая строка.
2. Настроить канал ДМА, чтобы во время обработки буферов (1.2.) и (1.1.) копировались данные из внешней памяти в (1.3.).
3. Дожидаемся окончания работы ДМА, свопим буфера и запускаем снова канал ДМА, а сами тем временем обрабатываем предыдущие строки.

Это несомненно правильный подход. Сейчас как раз разбираюсь с DMA
Цитата(qxov @ Nov 18 2007, 16:15) *
Здесь надо менять очень серьезно все. А уже потом - оптимизировать код, там тоже есть, где разгуляться.

В принципе да, но и оптимизацию до применения DMA можно сделать или параллельно ей, т. к. это, в общем-то, независимые задачи.
Цитата(qxov @ Nov 18 2007, 16:15) *
И, думаю, если копнуть глубже, то окажется, что времени не 1/50 секунды есть, а 1/25, не так ли? Реально частота кадров какая?

Частота кадров именно 50 Гц. Таково, так сказать, ТЗ и некуда от этого не деться. Но можно, до определённых пределов, уменьшить разрешение кадра.
Цитата(qxov @ Nov 19 2007, 23:42) *
Как успехи? Вот, набросал на коленке кусочек. Может, пригодиться...
...

Не совсем понял что делает этот кусок, т. е. какому участку C-го алгоритма он соответствует?
Degun
Уважаемый qxov я сделал вашу функцию calcModes с применением DMA. На изображении с разрешением 640*480 пикселей она выполняется за 10.2 мсек.
Код
#include <stdlib.h>
#include <csl_dat.h>

#define IMG_WIDTH  640
#define IMG_HEIGHT 480

// Буферы для кэширования строк изображения
static unsigned char ModesBuf[3][IMG_WIDTH];

//Функция, классифицирующая пикселы изображения по модам
void SplitImage2Modes(unsigned char ** restrict image, unsigned char ** restrict modes,
    int thresold1, int thresold2)
{
    //Рабочие регистры цикла
    int lockPixels, compareLo, compareHi, result1, result2;
    unsigned char * restrict pCurrLineBuf;
    double pixels;

    //Open DAT-chanel
    DAT_open(DAT_CHAANY, DAT_PRI_HIGH, 0);

    //Загрузка первой строки изображения
    Uint32 id_LoadTransfer = DAT_copy(image[0], ModesBuf[0], IMG_WIDTH);
    //Начальная инициализация идентификатора канала сохранения
    Uint32 id_SaveTransfer=DAT_XFRID_WAITNONE;

    //кольцевой указатель на буфер текущей строки
    int iCurrBufIndex=0;

    //пакеты порогов
    int thresoldLo=_packl4(_pack2(thresold1,thresold1),_pack2(thresold1,thresold1));
    int thresoldHi=_packl4(_pack2(thresold2,thresold2),_pack2(thresold2,thresold2));

    //цикл по всем строкам изображения
//#pragma MUST_ITERATE(IMG_HEIGHT, IMG_HEIGHT);
    for(int y=0; y<IMG_HEIGHT; y++)
    {
        //Указатель на текущую обрабатываемую строку
        pCurrLineBuf = ModesBuf[iCurrBufIndex];
        //Продвижение индекса на следующую строку
        iCurrBufIndex++; iCurrBufIndex%=3;
        //Ожидание окончания загрузки текущей строки изображения
        while(DAT_busy(id_LoadTransfer));
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer = DAT_copy(image[y+1], ModesBuf[iCurrBufIndex], IMG_WIDTH);
        //обработка текущей строки
//#pragma MUST_ITERATE(IMG_WIDTH/8, IMG_WIDTH/8);
        for(int x=0; x<IMG_WIDTH; x+=8)
        {
            // Загрузим сразу 8 пикселов
            pixels=_amemd8_const(pCurrLineBuf+x);                  // D

            //Первые четыре пиксела
            lockPixels=_lo(pixels);

            // Если пиксел >= thresold1, то в соответсвующем байте будет 0xff
            compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo));     // L + M

            // Если пиксел >= thresold2, то в соответсвующем байте будет 0xff
            compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi));     // L + M

            // Здесь очень удачно все сложилось - возможны три варианта:
            // если пиксел>=thresold1 и пиксел<thresold2, то получится 0xff ^ 0x00 = 0xff = -1;
            // если пиксел>=thresold1 и пиксел>=thresold2, то получится 0xff ^ 0xfe = 0x01
            // если пиксел<thresold, то пиксел<thresold2 и получается 0x00 ^ 0x00 = 0x00
            result1=compareLo ^ (compareHi & 0xfefefefe);          // LSD + LSD

            // Здесь все тоже самое, для оставшихся четырех пикселов
            lockPixels=_hi(pixels);

            compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo));     // L + M
            compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi));     // L + M

            result2=compareLo ^ (compareHi & 0xfefefefe);          // LSD + LSD

            // Сохраним результат
            _amemd8(pCurrLineBuf+x)=_itod(result2,result1);        // D

            // Здесь получаем, что 4L и 4M юнита - два цикла; еще осталось 4S и 4D, 2D на загрузку
            // уйдет -> остается 4S и 2D юнита, для внутренностей цикла надо 4LSD => останется два S
            // юнита на поддержание цикла. Теоретически, производительность кода - 2 такта на 8 пикселов
            // или 0.25 такта на пиксел. При этом удалось добиться соответствия со значениями мод из
            // оригинального кода, добавив лишь одно ограничение по - количество пикселей должно быть кратно 8.
            // Следует также отметить, что даже при больших размерах изображения, не возникает простоя из-за
            // того, что данные не находятся в кэше.
        }
        //Ожидание окончания сохранения результата
        while(DAT_busy(id_SaveTransfer));
        //Сохранение строки результата
        id_SaveTransfer = DAT_copy(pCurrLineBuf, modes[y], IMG_WIDTH);
    }

    //Ожидание окончания сохранения результата
    while(DAT_busy(id_SaveTransfer));

    //Close DAT-chanel
    DAT_close();
}

Для сравнения неоптимизированный код для того же изображения выполняется за 102 мсек, т. е. оптимизация даёт десятикратный выигрыш.
Код
#include <stdlib.h>

//Функция, классифицирующая пикселы изображения по модам
void SplitImage2Modes(unsigned char ** restrict image, unsigned char ** restrict modes,
    int height, int width, int thresold1, int thresold2)
{
    if ((image==NULL)||(modes==NULL)) return;

    //цикл по всем пикселам изображения
    for(int y=0; y<height; y++) for(int x=0; x<width; x++)
    {
        int pixel=image[y][x];
        if (pixel<thresold1)
            modes[y][x]=0;
        else if (pixel>thresold2)
            modes[y][x]=1;
        else
            modes[y][x]=-1;
    }
}
qxov
Цитата(Degun @ Nov 22 2007, 17:13) *
Уважаемый qxov я сделал вашу функцию calcModes с применением DMA. На изображении с разрешением 640*480 пикселей она выполняется за 10.2 мсек.

Для сравнения неоптимизированный код для того же изображения выполняется за 102 мсек, т. е. оптимизация даёт десятикратный выигрыш.

Скорее всего, простои на ожидание окончания работы DMA + накладные расходы, связанные с DAT модулем. Нужно интегрировать с какими-нибудь другими расчетами - тогда это замедление не будет так сильно сказываться. На глаз раз в 50 сами вычисления быстрее должны производиться, но это - грубо, могу ошибиться, просто спешу сейчас. Но точно сами вычисления значительно быстрее проходят.
qxov
А можно ли считать, что за пределами кадра есть рамка размером в один пиксел со значением, скажем, 0?
Degun
Цитата(qxov @ Nov 22 2007, 23:00) *
А можно ли считать, что за пределами кадра есть рамка размером в один пиксел со значением, скажем, 0?

Да вполне.

Цитата(qxov @ Nov 22 2007, 17:49) *
Скорее всего, простои на ожидание окончания работы DMA + накладные расходы, связанные с DAT модулем. Нужно интегрировать с какими-нибудь другими расчетами - тогда это замедление не будет так сильно сказываться. На глаз раз в 50 сами вычисления быстрее должны производиться, но это - грубо, могу ошибиться, просто спешу сейчас. Но точно сами вычисления значительно быстрее проходят.

Попробовал вариант, в котором убрал все обращения к внешней памяти через DMA, а только к внутреннему static буферу. Этот варинат выполнился за 7.9 мсек. Не понимаю тогда где задержка?
Код
#include <stdlib.h>

#include "cs_common.h"

// Буферы для кэширования строк изображения
static unsigned char ModesBuf[3][IMG_WIDTH];

//Функция, классифицирующая пикселы изображения по модам
void SplitImage2Modes(unsigned char ** restrict image, unsigned char ** restrict modes,
    int thresold1, int thresold2)
{
    //Рабочие регистры цикла
    int lockPixels, compareLo, compareHi, result1, result2;
    unsigned char * restrict pCurrLineBuf;
    double pixels;

    //кольцевой указатель на буфер текущей строки
    int iCurrBufIndex=0;

    //пакеты порогов
    int thresoldLo=_packl4(_pack2(thresold1,thresold1),_pack2(thresold1,thresold1));
    int thresoldHi=_packl4(_pack2(thresold2,thresold2),_pack2(thresold2,thresold2));

    //цикл по всем строкам изображения
//#pragma MUST_ITERATE(IMG_HEIGHT, IMG_HEIGHT);
    for(int y=0; y<IMG_HEIGHT; y++)
    {
        //Указатель на текущую обрабатываемую строку
        pCurrLineBuf = ModesBuf[iCurrBufIndex];
        //Продвижение индекса на следующую строку
        iCurrBufIndex++; iCurrBufIndex%=3;
        //обработка текущей строки
//#pragma MUST_ITERATE(IMG_WIDTH/8, IMG_WIDTH/8);
        for(int x=0; x<IMG_WIDTH; x+=8)
        {
            // Загрузим сразу 8 пикселов
            pixels=_amemd8_const(pCurrLineBuf+x);                  // D

            //Первые четыре пиксела
            lockPixels=_lo(pixels);

            // Если пиксел >= thresold1, то в соответсвующем байте будет 0xff
            compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo));     // L + M

            // Если пиксел >= thresold2, то в соответсвующем байте будет 0xff
            compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi));     // L + M

            // Здесь очень удачно все сложилось - возможны три варианта:
            // если пиксел>=thresold1 и пиксел<thresold2, то получится 0xff ^ 0x00 = 0xff = -1;
            // если пиксел>=thresold1 и пиксел>=thresold2, то получится 0xff ^ 0xfe = 0x01
            // если пиксел<thresold, то пиксел<thresold2 и получается 0x00 ^ 0x00 = 0x00
            result1=compareLo ^ (compareHi & 0xfefefefe);          // LSD + LSD

            // Здесь все тоже самое, для оставшихся четырех пикселов
            lockPixels=_hi(pixels);

            compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo));     // L + M
            compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi));     // L + M

            result2=compareLo ^ (compareHi & 0xfefefefe);          // LSD + LSD

            // Сохраним результат
            _amemd8(pCurrLineBuf+x)=_itod(result2,result1);        // D
        }
    }
}
qxov
Цитата(Degun @ Nov 23 2007, 14:58) *
Да вполне.
Попробовал вариант, в котором убрал все обращения к внешней памяти через DMA, а только к внутреннему static буферу. Этот варинат выполнился за 7.9 мсек. Не понимаю тогда где задержка?

Точно 7.9 мсек?
Просто 7.9 мсек на 500МГц - 3950000 циклов, что многовато для обработки 640*480=307200 пикселов изображения - более 128 циклов на пиксел, а расчетное - около 0.25 с хвостиком должно выходить. Ну пусть в два раза ошиблись, но не на столько же... Оптимизация-то включена? smile.gif
Degun
Цитата(qxov @ Nov 23 2007, 15:50) *
Точно 7.9 мсек?
Просто 7.9 мсек на 500МГц - 3950000 циклов, что многовато для обработки 640*480=307200 пикселов изображения - более 128 циклов на пиксел, а расчетное - около 0.25 с хвостиком должно выходить. Ну пусть в два раза ошиблись, но не на столько же... Оптимизация-то включена? smile.gif

Точно 7.9 мсек. Может быть где-то в настройках DSP-BIOS стоит опция размещать статические буфера не в IRAM, а в во внешней SDRAM. Возможно именно из-за этого такое большое время выполнения.
qxov
Цитата(Degun @ Nov 25 2007, 20:44) *
Точно 7.9 мсек. Может быть где-то в настройках DSP-BIOS стоит опция размещать статические буфера не в IRAM, а в во внешней SDRAM. Возможно именно из-за этого такое большое время выполнения.

Я в DSP-BIOS не верю smile.gif Не могу ничего сказать по этому поводу - не приходилось сколько-нибудь значимых с ним дел иметь.
Degun
Цитата(qxov @ Nov 26 2007, 10:41) *
Я в DSP-BIOS не верю smile.gif Не могу ничего сказать по этому поводу - не приходилось сколько-нибудь значимых с ним дел иметь.

Точно в DSP-BIOS было дело. Изменил настройки и тот вариант с DMA, который я приводил выше, стал выполняться за 0.967 мсек
qxov
Не знаю, на сколько этот вариант подходит:

если min(abs(дельта яркости окрестных точек))>(thresoldHi-thresoldLo), то новая область;
иначе - точка относится к области, соответствующей одной из min(abs(дельта яркости окрестных точек))
Degun
Цитата(qxov @ Nov 26 2007, 22:07) *
Не знаю, на сколько этот вариант подходит:

если min(abs(дельта яркости окрестных точек))>(thresoldHi-thresoldLo), то новая область;
иначе - точка относится к области, соответствующей одной из min(abs(дельта яркости окрестных точек))

Вообще-то интересный вариант. Не понятно только как начинать разметку изображения, когда ещё никаких областей нет? А чем вообще обусловлен вопрос - этот вариант проще?
qxov
Цитата(Degun @ Nov 27 2007, 15:26) *
Вообще-то интересный вариант. Не понятно только как начинать разметку изображения, когда ещё никаких областей нет? А чем вообще обусловлен вопрос - этот вариант проще?

Начинать просто - первому пикселу задаем какой-то номер области и от этого пляшем.
Вариант значительно проще - в том алгоритме, который используется сейчас, это так или иначе все равно делается, но помимо этих расчетов еще куча других присутствует. Если данная схема позволит полчить удовлетворительные результаты, то, наверное, лучше копать в этом направлении. Разумеется, потом все равно придется как-то перенумеровывать области точек. Хотя, если предположить, что исходная картинка такова, что при сканировании ее по строкам и задания областей не может возникнуть случая, подобного
Код
..11..22..
...11.2...
....11....

либо допускается, что области, содержащие точки, скорее относящиеся к одинаковым модам, соприкасаются, не объединяясь при этом в одну, то алгоритм можно упростить до безумия smile.gif
Degun
Цитата(qxov @ Nov 28 2007, 09:07) *
Начинать просто - первому пикселу задаем какой-то номер области и от этого пляшем.
Вариант значительно проще - в том алгоритме, который используется сейчас, это так или иначе все равно делается, но помимо этих расчетов еще куча других присутствует. Если данная схема позволит полчить удовлетворительные результаты, то, наверное, лучше копать в этом направлении. Разумеется, потом все равно придется как-то перенумеровывать области точек. Хотя, если предположить, что исходная картинка такова, что при сканировании ее по строкам и задания областей не может возникнуть случая, подобного
Код
..11..22..
...11.2...
....11....

Такие случаи, к сожалению, встречаются и довольно часто. Так, что без перенумеровывания, по-видимому, не обойтись.
Цитата(qxov @ Nov 28 2007, 09:07) *
либо допускается, что области, содержащие точки, скорее относящиеся к одинаковым модам, соприкасаются, не объединяясь при этом в одну, то алгоритм можно упростить до безумия smile.gif

Так тоже не честно. В этом суть алгоритма разметки на области.
Николай Z
Цитата(Degun @ Oct 23 2007, 14:54) *
Code Composer Studio v 3.3; Процессор DM642 на evalution board.
Имеется программка, написанная на C++. После анализа её ассемблерного кода выяснилось, что после многих команд (LDW, ADD, CMPLT, AND, LDHU и др.) компилятор вставляет команду NOP, что естественно замедляет функционирование программы. Для чего это? Можно ли отключить вставку операторов NOP в ассемблерный код?


Прочтите доку на процессор и Вам станет понятно назначение NOP-ов, которые Вам в код всовывает компилятор...

Я не знаю Вашего процессора, но это достаточно стандартный прием во многих микроконтроллерах, которые работают по принципу конвейера...

Делается - чаще всего для того, чтобы дать возможность процессору закончить записб результата на регистры управления или в регистры/ячейки внешней памяти... Т.к. иначе - возможно использование "старого" значения в следующей команде...

Вышесказанное - это лишь возможные причины - настоящие - в документации на Ваш микроконтроллер...

Попытки убрать такие NOP-ы могут привести к сбоям, а могут и остаться безнаказанными - все зависит от операндов следующих за NOP-ами команд...

В любом случае - прежде чем убирать такие NOP-ы - надо как следует подумать...
Если компилятор "умный" - то он сам анализирует - надо вставлять NOP-ы или нет...
Если глупый - он вставляет их "не думая" в критических местах так - как это решил нужным сделать разработчик компилятора и возможно - часть их лишние...

Общая рекомендация - лучше их не убирать... Не так уж много времени они обчно забирают
qxov
Цитата(Degun @ Dec 6 2007, 20:40) *
Такие случаи, к сожалению, встречаются и довольно часто. Так, что без перенумеровывания, по-видимому, не обойтись.

Так тоже не честно. В этом суть алгоритма разметки на области.

Я уже потом подумал, что, наверное, схема с разметкой по превышению порога изменения яркости будет работать плохо - нужна высокая контрастность соседних точек, иначе все одной областью заполнится тупо.
Degun
Вот такой у меня получился код для DSP. Но для изображения размером 640*480 пикселей он выполняется за 85 мсек, что много. Что здесь можно ещё оптимизировать?

Код
#include <stdlib.h>
#include <string.h>
#include <csl_dat.h>

//Максимальное кол-во областей в изображении
#define MAX_AREAS 1000
//Максимальная ширина изображения
#define IMG_WIDTH_MAX  640
//Максимальная высота изображения
#define IMG_HEIGHT_MAX 480
//Макросы минимума и максимума
#define max(a, b)  (((a) > (b)) ? (a) : (b))
#define min(a, b)  (((a) < (b)) ? (a) : (b))


//Счётчики количества областей в изображении и в контейнере
static int iAreasCount=0, iAreasInfoCount=0;

//Ограниченные массивы структур описателей областей!!!
static short AreasMode[MAX_AREAS+1];
static short AreasNumber[MAX_AREAS+1];

//Функция создания новой области
static inline int AddNewAreaInfo(const int iModeNumb)
{
//    if (iAreasInfoCount>=MAX_AREAS) return -1;
    iAreasCount++;
    AreasMode[iAreasInfoCount]=iModeNumb;
    AreasNumber[iAreasInfoCount]=-1;
    return iAreasInfoCount++;
}

// Буферы для кэширования строк изображения
static unsigned char ImageBuf[3][IMG_WIDTH_MAX];
static int AreasBuf[2][IMG_WIDTH_MAX];


int test_SplitImage2Areas(
    register unsigned char ** restrict Image,
    register int Height, register int Width,
    register short * restrict ModeNumb4Pix,
    register int ** restrict Areas)
{
    //Изображение последовательно просматривается по строкам
    //для отнесения каждого пиксела к определённой области
    //Вычисление площади, занимаемой каждой областью и модой
    register int iPrevAreaNumbL=-1;  //Номер области предыдущей левой точки
    register int iPrevModeNumbL=-1;  //Номер моды предыдущей левой точки
    register int iPrevAreaNumbU=-1;  //Номер области предыдущей верхней точки
    register int iPrevModeNumbU=-1;  //Номер моды предыдущей верхней точки
    register int iPrevAreaNumbUL=-1; //Номер области предыдущей верхней левой точки
    register int iPrevModeNumbUL=-1; //Номер моды предыдущей верхней левой точки
    register int iPrevAreaNumbUR=-1; //Номер области предыдущей верхней правой точки
    register int iPrevModeNumbUR=-1; //Номер моды предыдущей верхней правой точки
    //Временные переменные
    register int i, y, x, iSrcIndex, iDstIndex, iDelta, iTmp;
    register int iCurrPixel, iCurrAreaNumb, iCurrModeNumb;
    register Uint32 id_LoadTransfer, id_SaveTransfer;
    register int iImageBufIndex, iAreasBufIndex;
    register unsigned char * restrict pImageCurrBuf;
    register unsigned char * restrict pImagePrevBuf;
    register int * restrict pAreasCurrBuf;
    register int * restrict pAreasPrevBuf;

    //Проверка корректности входных параметров
    if ((Image==0)||(ModeNumb4Pix==0)||(Areas==0)) return -1;
    if ((Width<=0)||(Height<=0)||(Width>IMG_WIDTH_MAX)||(Height>IMG_HEIGHT_MAX)) return -1;

    //Загрузка первой строки изображения
    id_LoadTransfer=DAT_copy(Image[0], ImageBuf[0], Width);

    //Обнуление счётчиков кол-ва областей
    iAreasCount=iAreasInfoCount=0;
    //Обработка верхней левой точки изображения
    iCurrPixel=Image[0][0];
    iCurrModeNumb=ModeNumb4Pix[iCurrPixel];
    if (iCurrModeNumb<0)
    {
        //Сканирование зигзагом окрестных точек для
        //выявления ближайшей с установленной модой
        iDelta=min(Width,Height);
        for (i=1; i<iDelta; i++)
        {
            iSrcIndex=-1;
            for (y=i, x=0; y>=0; y--, x++)
            {
                iDstIndex=Image[y][x];
                iTmp=ModeNumb4Pix[iDstIndex];
                if (iTmp<0) continue;
                iDstIndex=abs(iDstIndex-iCurrPixel);
                if ((iSrcIndex<0)||(iSrcIndex>iDstIndex))
                {
                    iSrcIndex=iDstIndex;
                    iCurrModeNumb=iTmp;
                }
            }
            //Нашли подходящую точку - выход из поиска
            if (iCurrModeNumb>=0) break;
        }
        //Если точек нет, то нулевая мода
        if (iCurrModeNumb<0) iCurrModeNumb=0;
    }
    iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
    AreasBuf[0][0]=iCurrAreaNumb;
    //Указатель на текущую обрабатываемую строку изображения
    pImageCurrBuf=ImageBuf[0];
    //Указатель на текущую результирующую строку номеров областей
    pAreasCurrBuf=AreasBuf[0];
    //Ожидание окончания загрузки первой строки изображения
    while(DAT_busy(id_LoadTransfer));
    //Запуск загрузки второй строки изображения
    id_LoadTransfer=DAT_copy(Image[1], ImageBuf[1], Width);
    //Обработка первой верхней строки изображения,
    //начиная со второго пиксела строки
    for (x=1; x<Width; x++)
    {
        //Запоминание номера области и моды для левой точки
        iPrevAreaNumbL=iCurrAreaNumb;
        iPrevModeNumbL=iCurrModeNumb;
        //Номер моды текущей точки
        iCurrModeNumb=ModeNumb4Pix[pImageCurrBuf[x]];
        //В зависимости от наличия моды
        if (iCurrModeNumb<0) {
            iCurrModeNumb=iPrevModeNumbL;
            iCurrAreaNumb=iPrevAreaNumbL;
        }
        else if (iCurrModeNumb==iPrevModeNumbL) {
            iCurrAreaNumb=iPrevAreaNumbL;
        }
        else {
            //Добавление новой области
            iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
        }
        //Установка номера области текущей точки
        pAreasCurrBuf[x]=iCurrAreaNumb;
    }
    //Сохранение первой строки результата
    id_SaveTransfer=DAT_copy(AreasBuf[0], Areas[0], Width);
    //установка кольцевого указателя на буфер текущей строки
    iImageBufIndex=iAreasBufIndex=1;
    //Декремент ширины изображения для увеличения скорости обработки
    Width--;
    //Цикл по всем точкам изображения
    for (y=1; y<Height; y++)
    {
        //Сохранение указателя на предыдущую строку изображения
        pImagePrevBuf=pImageCurrBuf;
        //Сохранение указателя на предыдущую строку результата
        pAreasPrevBuf=pAreasCurrBuf;
        //Указатель на текущую строку изображения
        pImageCurrBuf=ImageBuf[iImageBufIndex];
        //Указатель на текущую строку результата
        pAreasCurrBuf=AreasBuf[iAreasBufIndex];
        //Продвижение индекса на следующую строку
        iImageBufIndex++; iImageBufIndex%=3;
        iAreasBufIndex++; iAreasBufIndex%=2;
        //Ожидание окончания загрузки текущей строки изображения
        while(DAT_busy(id_LoadTransfer));
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer = DAT_copy(Image[y+1], ImageBuf[iImageBufIndex], Width);
        //Установка начального значения для верхней левой точки
        iPrevAreaNumbUL=pAreasPrevBuf[0];
        iPrevModeNumbUL=AreasMode[iPrevAreaNumbUL];
        //Установка начального значения для верхней точки
        iPrevAreaNumbU=pAreasPrevBuf[1];
        iPrevModeNumbU=AreasMode[iPrevAreaNumbU];
        //Значение яркости первого пикселя строки
        iCurrPixel=pImageCurrBuf[0];
        //Значение моды первого пикселя строки
        iPrevModeNumbL=ModeNumb4Pix[iCurrPixel];
        //В зависимости от признака переходности пиксела
        if (iPrevModeNumbL<0) {
            if (abs(iCurrPixel-pImagePrevBuf[0])<abs(iCurrPixel-pImagePrevBuf[1]))
            {
                iPrevModeNumbL=iPrevModeNumbUL;
                iPrevAreaNumbL=iPrevAreaNumbUL;
            }
            else
            {
                iPrevModeNumbL=iPrevModeNumbU;
                iPrevAreaNumbL=iPrevAreaNumbU;
            }
        }
        else if (iPrevModeNumbL==iPrevModeNumbUL)
            iPrevAreaNumbL=iPrevAreaNumbUL; //Верхняя левая точка
        else if (iPrevModeNumbL==iPrevModeNumbU)
            iPrevAreaNumbL=iPrevAreaNumbU;  //Верхняя точка
        else {
            //Добавление новой области
            iPrevAreaNumbL=AddNewAreaInfo(iPrevModeNumbL);
        }
        //Установка номера области первого пиксела строки
        pAreasCurrBuf[0] = iPrevAreaNumbL;
        //Обработка очередной строки изображения
        for (x=1; x<Width; x++)
        {
            //Определение параметров верхней правой точки
            iPrevAreaNumbUR=pAreasPrevBuf[x+1];
            iPrevModeNumbUR=AreasMode[iPrevAreaNumbUR];
            //Значение яркости текущего пикселя
            iCurrPixel=pImageCurrBuf[x];
            //Номер моды текущей точки
            iCurrModeNumb=ModeNumb4Pix[iCurrPixel];
            //В зависимости от того является ли точка переходной
            if (iCurrModeNumb<0)
            {
                //Текущая точка является переходной к любой области
                //Перебор всех точек окрестности и выбор наиболее близкой области
                //Левая точка
                iDelta=abs(pImageCurrBuf[x-1]-iCurrPixel);
                iCurrAreaNumb=iPrevAreaNumbL;
                iCurrModeNumb=iPrevModeNumbL;
                //Верхняя точка
                iTmp=abs(pImagePrevBuf[x]-iCurrPixel);
                if (iDelta>iTmp) {
                    iDelta=iTmp;
                    iCurrAreaNumb=iPrevAreaNumbU;
                    iCurrModeNumb=iPrevModeNumbU;
                }
                //Верхняя левая точка
                iTmp=abs(pImagePrevBuf[x-1]-iCurrPixel);
                if (iDelta>iTmp) {
                    iDelta=iTmp;
                    iCurrAreaNumb=iPrevAreaNumbUL;
                    iCurrModeNumb=iPrevModeNumbUL;
                }
                //Верхняя правая точка
                iTmp=abs(pImagePrevBuf[x+1]-iCurrPixel);
                if (iDelta>iTmp) {
                    iDelta=iTmp;
                    iCurrAreaNumb=iPrevAreaNumbUR;
                    iCurrModeNumb=iPrevModeNumbUR;
                }
            }
            //Эта точка не является переходной ни к одной из мод
            //Просмотр всех соседних уже размеченных ранее точек
            //для возможного определения номера области текущей точки
            else if (iCurrModeNumb==iPrevModeNumbL)
                    iCurrAreaNumb=iPrevAreaNumbL;  //Левая точка
            else if (iCurrModeNumb==iPrevModeNumbUL)
                    iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка
            else if (iCurrModeNumb==iPrevModeNumbU)
                    iCurrAreaNumb=iPrevAreaNumbU;  //Верхняя точка
            else if (iCurrModeNumb==iPrevModeNumbUR)
                    iCurrAreaNumb=iPrevAreaNumbUR; //Верхняя правая точка
            else {
                //Добавление новой области
                iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
            }
            //Установка номера области текущей точки
            pAreasCurrBuf[x] = iCurrAreaNumb;
            //Сканирование верхней правой точки для добавления её в список соседних областей
            if ((iCurrModeNumb==iPrevModeNumbUR)&&(iCurrAreaNumb!=iPrevAreaNumbUR))
            {
                //Всего четыре возможных варианта
                if (AreasNumber[iCurrAreaNumb]<0)
                {
                    if (AreasNumber[iPrevAreaNumbUR]>=0)
                    {
                        //Только добавляется ссылка на ссылочную область
                        AreasNumber[iCurrAreaNumb]=AreasNumber[iPrevAreaNumbUR];
                        //Декремент кол-ва областей
                        iAreasCount--;
                    }
                    else
                    {
                        //Обе области являются доменными
                        iTmp=min(iCurrAreaNumb,iPrevAreaNumbUR);
                        AreasNumber[iCurrAreaNumb]=iTmp;
                        AreasNumber[iPrevAreaNumbUR]=iTmp;
                        //Декремент кол-ва областей
                        iAreasCount--;
                    }
                }
                else //if (AreasNumber[iCurrAreaNumb]>=0)
                {
                    if (AreasNumber[iPrevAreaNumbUR]<0)
                    {
                        //Только добавляется ссылка на ссылочную область
                        AreasNumber[iPrevAreaNumbUR]=AreasNumber[iCurrAreaNumb];
                        //Декремент кол-ва областей
                        iAreasCount--;
                    }
                    else
                    {
                        iSrcIndex=AreasNumber[iCurrAreaNumb];
                        iDstIndex=AreasNumber[iPrevAreaNumbUR];
                        if (iSrcIndex!=iDstIndex)
                        {
                            //Это самый трудоёмкий случай.
                            //Но, к счастью, самый редкий!
                            //Необходимо объединить две области
                            if (iSrcIndex<iDstIndex)
                            {
                                //Обмен значениями
                                iTmp=iSrcIndex;
                                iSrcIndex=iDstIndex;
                                iDstIndex=iTmp;
                            }
                            for (i=0; i<iAreasInfoCount; i++)
                            {
                                if (AreasNumber[i]==iSrcIndex)
                                    AreasNumber[i]=iDstIndex;
                            }
                            //Декремент кол-ва областей
                            iAreasCount--;
                        }
                    }
                }
            }
            //Запоминание номера области и моды для левой точки
            iPrevAreaNumbL=iCurrAreaNumb;
            iPrevModeNumbL=iCurrModeNumb;
            //Запоминание номера области и моды для верхней левой точки
            iPrevAreaNumbUL=iPrevAreaNumbU;
            iPrevModeNumbUL=iPrevModeNumbU;
            //Запоминание номера области и моды для верхней точки
            iPrevAreaNumbU=iPrevAreaNumbUR;
            iPrevModeNumbU=iPrevModeNumbUR;
        }
        //Значение яркости последнего пикселя строки
        iCurrPixel=pImageCurrBuf[Width];
        //Значение моды последнего пикселя строки
        iCurrModeNumb=ModeNumb4Pix[iCurrPixel];
        //В зависимости от наличия моды
        if (iCurrModeNumb<0) {
            //Левая точка
            iDelta=abs(pImageCurrBuf[x-1]-iCurrPixel);
            iCurrAreaNumb=iPrevAreaNumbL;
            iCurrModeNumb=iPrevModeNumbL;
            //Верхняя левая точка
            iTmp=abs(pImagePrevBuf[x-1]-iCurrPixel);
            if (iDelta>iTmp) {
                iDelta=iTmp;
                iCurrAreaNumb=iPrevAreaNumbUL;
                iCurrModeNumb=iPrevModeNumbUL;
            }
            //Верхняя точка
            iTmp=abs(pImagePrevBuf[x]-iCurrPixel);
            if (iDelta>iTmp) {
                iDelta=iTmp;
                iCurrAreaNumb=iPrevAreaNumbU;
                iCurrModeNumb=iPrevModeNumbU;
            }
        }
        else if (iCurrModeNumb==iPrevModeNumbL)
            iCurrAreaNumb=iPrevAreaNumbL;  //Левая точка
        else if (iCurrModeNumb==iPrevModeNumbUL)
            iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка
        else if (iCurrModeNumb==iPrevModeNumbU)
            iCurrAreaNumb=iPrevAreaNumbU;  //Верхняя правая точка
        else {
            //Добавление новой области
            iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb);
        }
        //Установка номера области последнего пикселя строки
        pAreasCurrBuf[Width]=iCurrAreaNumb;
        //Ожидание окончания сохранения результата
        while(DAT_busy(id_SaveTransfer));
        //Сохранение строки результата
        id_SaveTransfer=DAT_copy(pAreasCurrBuf, Areas[y], Width);
    }
    //Восстановление исходной ширины изображения
    Width++;

    //Упорядочивание переадресации областей (именно здесь)!!!!!
    for (i=0; i<iAreasInfoCount; i++)
    {
        //Номер кластера для текущей области
        iTmp=AreasNumber[i];
        //Пропуск кластерных областей
        if (iTmp<0)
            //Установка номера области
            AreasNumber[i]=i;
        else
            //Выключение области
            AreasMode[i]=-1;
    }

    //Могут оставаться независимые области с индексами больше iAreasCount
    for (iDstIndex=0, iSrcIndex=iAreasCount; iSrcIndex<iAreasInfoCount; iSrcIndex++)
    {
        //Если эта область уже переадресована - все нормально
        if (AreasMode[iSrcIndex]<0) continue;
        //Поиск индекса для переадресации
        for (/*задаётся вовне*/; iDstIndex<iAreasCount; iDstIndex++)
            if (AreasMode[iDstIndex]<0) break;
        //Сохранение указателя на моду
        AreasMode[iDstIndex]=AreasMode[iSrcIndex];
        //Выключение переадресуемой области
        AreasMode[iSrcIndex]=-1;
        //Установка ссылки данной области на переадресованную
        AreasNumber[iSrcIndex]=iDstIndex;
        //Замена всех ссылок на эту область!!!!!!!!
        for (i=0; i<iAreasInfoCount; i++)
            if (AreasNumber[i]==iSrcIndex) AreasNumber[i]=iDstIndex;
    }

    //Загрузка первой строки номеров областей
    id_LoadTransfer=DAT_copy(Areas[0], AreasBuf[0], Width);
    //Установка кольцевого указателя
    iAreasBufIndex=0;
    //Начальная инициализация идентификатора канала сохранения
    id_SaveTransfer=DAT_XFRID_WAITNONE;
    //Обработка всех строк
    for (y=0; y<Height; y++)
    {
        //Указатель на текущую строку результата
        pAreasCurrBuf=AreasBuf[iAreasBufIndex];
        //Продвижение индекса на следующую строку
        iAreasBufIndex^=1;
        //Ожидание окончания загрузки текущей строки изображения
        while(DAT_busy(id_LoadTransfer));
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer=DAT_copy(Areas[y+1], AreasBuf[iAreasBufIndex], Width);
        //Переписывание индексов удалённых областей на переназначенные
        for (x=0; x<Width; x++) pAreasCurrBuf[x]=AreasNumber[pAreasCurrBuf[x]];
        //Ожидание окончания сохранения результата
        while(DAT_busy(id_SaveTransfer));
        //Сохранение строки результата
        id_SaveTransfer=DAT_copy(pAreasCurrBuf, Areas[y], Width);
    }
    //Ожидание окончания сохранения результата
    while(DAT_busy(id_SaveTransfer));

    return iAreasCount;
}
qxov
Очень тяжелый вложенный цикл. Нужно рассмотреть возможность упрощения. Возможно, имеет смысл разбить его на несколько значительно более простых.
Degun
Цитата(qxov @ Feb 13 2008, 13:16) *
Очень тяжелый вложенный цикл. Нужно рассмотреть возможность упрощения. Возможно, имеет смысл разбить его на несколько значительно более простых.

Значительно упростил вложенный цикл за счёт чуть большего расхода памяти. В результате для изображения с разрешением 640*480 процедура стала выполняться за 55 мсек. Но всё равно как-то много. Чтобы ещё такого оптимизировать?
Код
#include <stdlib.h>
#include <limits.h>
#include <csl_dat.h>

//Максимальное кол-во областей в изображении
#define MAX_AREAS 2000

//Ограниченный массив структур описателей областей!!!
#pragma DATA_ALIGN(8);
static short AreasMode[MAX_AREAS+1];
#pragma DATA_ALIGN(8);
static short AreasNumber[MAX_AREAS+1];
#pragma DATA_ALIGN(8);
static short AreasPixel[MAX_AREAS+1];

//Буферы для кэширования строк изображения
#pragma DATA_ALIGN(8);
static unsigned char ImageBuf[2][IMG_WIDTH_MAX];
#pragma DATA_ALIGN(8);
static unsigned short AreasBuf[2][IMG_WIDTH_MAX];


int dsp_SplitImage2AreasFast(
    unsigned char ** restrict Image,
    int Height, int Width,
    unsigned char *  restrict ModeNumb4Pix,
    unsigned short ** restrict Areas)
{
    //Изображение последовательно просматривается по строкам
    //для отнесения каждого пиксела к определённой области
    //Вычисление площади, занимаемой каждой областью и модой
    register int iAreasCounter;    //Счётчик количества областей в изображении
    register int iAreasInfoCounter;//Счётчик количества областей в контейнере
    register int iPrevAreaNumbL;   //Номер области предыдущей левой точки
    register int iPrevModeNumbL;   //Номер моды предыдущей левой точки
    //Временные переменные
    register int i, y, x, iSrcIndex, iDstIndex, iTmp;
    register int iCurrAreaNumb, iCurrModeNumb, iAreasInfoCountSav;
    register Uint32 id_LoadTransfer, id_SaveTransfer;
    register unsigned char * restrict pImageNextBuf;
    register unsigned char * restrict pImageCurrBuf;
    register unsigned short * restrict pAreasCurrBuf;
    register unsigned short * restrict pAreasPrevBuf;

    //Проверка корректности входных параметров
    if ((Image==0)||(ModeNumb4Pix==0)||(Areas==0)||(Width<=0)||(Height<=0)||
        (Width>IMG_WIDTH_MAX)||(Height>IMG_HEIGHT_MAX)) return -1;

    //Установка указателя на следующую строку изображения
    pImageNextBuf=ImageBuf[0];
    //Загрузка первой строки изображения
    id_LoadTransfer=DAT_copy(Image[0], pImageNextBuf, Width);
    //Начальная инициализация идентификатора канала сохранения
    id_SaveTransfer=DAT_XFRID_WAITNONE;

    //Обнуление счётчиков кол-ва областей
    iAreasCounter=iAreasInfoCounter=0;
    //Установка кол-ва областей максимально возможное для первой строки
    iAreasInfoCountSav=INT_MAX;
    //Обнуление указателя на текущую строку
    pAreasCurrBuf=NULL;
    //Цикл по всем точкам изображения
    for (y=0; y<Height; y++)
    {
        //Сохранение указателя на текущую строку изображения
        pImageCurrBuf=pImageNextBuf;
        //Сохранение указателя на предыдущую строку результата
        pAreasPrevBuf=pAreasCurrBuf;
        //Указатель на следующую строку изображения
        pImageNextBuf=ImageBuf[(y+1)&1];
        //Указатель на текущую строку результата
        pAreasCurrBuf=AreasBuf[y&1];
        //Начальные значения номера области и моды для левой точки
        iPrevModeNumbL=iPrevAreaNumbL=-1;
        //Ожидание окончания загрузки текущей строки изображения
        DAT_wait(id_LoadTransfer);
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer = DAT_copy(Image[y+1], pImageNextBuf, Width);
        //Обработка очередной строки изображения
        for (x=0; x<Width; x++)
        {
            //Номер моды текущей точки
            iCurrModeNumb=ModeNumb4Pix[pImageCurrBuf[x]];
            //В зависимости от того принадлежит ли точка той же моде,
            //что и её левая точка, установка номера области
            if (iCurrModeNumb==iPrevModeNumbL)
                iCurrAreaNumb=iPrevAreaNumbL;
            else
            {
                //Добавление новой области
//                if (iAreasInfoCount>=MAX_AREAS) return -1;
                iCurrAreaNumb=iAreasInfoCounter;
                AreasMode[iAreasInfoCounter]=iCurrModeNumb;
                AreasNumber[iAreasInfoCounter]=iAreasInfoCounter;
                AreasPixel[iAreasInfoCounter]=x;
                //Инкремент счётчиков областей
                iAreasCounter++; iAreasInfoCounter++;
                //Запоминание номера области и моды для левой точки
                iPrevAreaNumbL=iCurrAreaNumb;
                iPrevModeNumbL=iCurrModeNumb;
            }
            //Установка номера области текущей точки
            pAreasCurrBuf[x]=iCurrAreaNumb;
        }
        //Обработка всех областей в текущей строке для выявления их связей
        //с областями на предыдущей строке
        for (; iAreasInfoCountSav<iAreasInfoCounter; iAreasInfoCountSav++)
        {
            iSrcIndex=iAreasInfoCountSav;
            iCurrModeNumb=AreasMode[iAreasInfoCountSav];
            iTmp=AreasPixel[iAreasInfoCountSav];
            if (iTmp>0) iTmp--;
            if (iAreasInfoCountSav==(iAreasInfoCounter-1))
                iCurrAreaNumb=Width-1;
            else
                iCurrAreaNumb=AreasPixel[iAreasInfoCountSav+1];
            iCurrAreaNumb=pAreasPrevBuf[iCurrAreaNumb];
            //Цикл по всем граничащим на предыдущей строке областям
            for (i=pAreasPrevBuf[iTmp]; i<=iCurrAreaNumb; i++)
            {
                if (AreasMode[i]!=iCurrModeNumb) continue;
                //На предыдущей строке найдена область с совпадающей модой,
                //которая соседствует с исходной
                iDstIndex=i;
                while(AreasNumber[iDstIndex]!=iDstIndex) iDstIndex=AreasNumber[iDstIndex];
                if (iSrcIndex!=iDstIndex)
                {
                    //Объединение двух кластеров областей
                    if (iSrcIndex<iDstIndex)
                    {
                        //Сохранение номера
                        AreasNumber[iDstIndex]=iSrcIndex;
                    }
                    else
                    {
                        //Сохранение номера
                        AreasNumber[iSrcIndex]=iDstIndex;
                        //Для дальнейшего объединения берётся меньший индекс
                        iSrcIndex=iDstIndex;
                    }
                    //Декремент кол-ва областей
                    iAreasCounter--;
                }
            }
        }
        //Сохранение кол-ва областей
        iAreasInfoCountSav=iAreasInfoCounter;
        //Ожидание окончания сохранения результата
        DAT_wait(id_SaveTransfer);
        //Сохранение строки результата
        id_SaveTransfer=DAT_copy(pAreasCurrBuf, Areas[y], sizeof(short)*Width);
    }

    //Обновление ссылок на области
    for (iSrcIndex=0; iSrcIndex<iAreasInfoCounter; iSrcIndex++)
    {
        //Поиск индекса назначения
        iDstIndex=iSrcIndex;
        while(AreasNumber[iDstIndex]!=iDstIndex) iDstIndex=AreasNumber[iDstIndex];
        //Обновление номера области
        AreasNumber[iSrcIndex]=iDstIndex;
    }

    //Могут оставаться независимые области с индексами больше iAreasCount
    for (iDstIndex=-1, iSrcIndex=iAreasCounter; iSrcIndex<iAreasInfoCounter; iSrcIndex++)
    {
        //Если эта область уже переадресована - все нормально
        if (AreasNumber[iSrcIndex]!=iSrcIndex) continue;
        //Поиск индекса для переадресации
        for (iDstIndex++; iDstIndex<iAreasCounter; iDstIndex++)
            if (AreasNumber[iDstIndex]!=iDstIndex) break;
        //Сохранение указателя на моду
        AreasMode[iDstIndex]=AreasMode[iSrcIndex];
        //Установка ссылки данной области на переадресованную
        AreasNumber[iSrcIndex]=iDstIndex;
        //Замена всех ссылок на эту область!!!!!!!!
        for (i=iSrcIndex; i<iAreasInfoCounter; i++)
            if (AreasNumber[i]==iSrcIndex) AreasNumber[i]=iDstIndex;
    }

    //Установка указателя на текущую загружаемую строку
    pAreasCurrBuf=AreasBuf[0];
    //Загрузка первой строки номеров областей
    id_LoadTransfer=DAT_copy(Areas[0], pAreasCurrBuf, sizeof(short)*Width);
    //Начальная инициализация идентификатора канала сохранения
    id_SaveTransfer=DAT_XFRID_WAITNONE;
    //Обработка всех строк
    for (y=0; y<Height; y++)
    {
        //Сохранение указателя на предыдущую строку результата
        pAreasPrevBuf=pAreasCurrBuf;
        //Указатель на следующую строку результата
        pAreasCurrBuf=AreasBuf[(y+1)&1];
        //Ожидание окончания загрузки текущей строки изображения
        DAT_wait(id_LoadTransfer);
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer=DAT_copy(Areas[y+1], pAreasCurrBuf, sizeof(short)*Width);
        //Переписывание индексов удалённых областей на переназначенные
        for (x=0; x<Width; x++) pAreasPrevBuf[x]=AreasNumber[pAreasPrevBuf[x]];
        //Ожидание окончания сохранения результата
        DAT_wait(id_SaveTransfer);
        //Сохранение строки результата
        id_SaveTransfer=DAT_copy(pAreasPrevBuf, Areas[y], sizeof(short)*Width);
    }
    //Ожидание окончания сохранения результата
    DAT_wait(id_SaveTransfer);

    return iAreasCounter;
}
Degun
Следующий вариант выполняется на 1 мсек быстрее, чем предыдущий. Возможно это ещё не предел оптимизации, но больше у меня идей нет. Если кто знает, то подскажите.
Код
/*!\brief Разбиение изображения на непересекающиеся области
* \brief в соответствии с таблицей мод
* \param Image - исходное изображение
* \param Height - высота изображения
* \param Width - ширина изображение
* \param ModeNumb4Pix - таблица соответствия значений пикселов модам гистограммы
* \param Areas - буфер результата, отражающий номера областей каждого из пикселов
* \result Количество непересекающихся областей в изображении
*/
#include <stdlib.h>
#include <csl_dat.h>

//Максимальное кол-во областей в изображении
#define MAX_AREAS 2000

//Ограниченный массив структур описателей областей!!!
#pragma DATA_ALIGN(8);
static short AreasMode[MAX_AREAS+1];
#pragma DATA_ALIGN(8);
static short AreasNumber[MAX_AREAS+1];
#pragma DATA_ALIGN(8);
static short AreasPixel[MAX_AREAS+1];

//Буферы для кэширования строк изображения
#pragma DATA_ALIGN(8);
static unsigned char ImageBuf[2][IMG_WIDTH_MAX];
#pragma DATA_ALIGN(8);
static unsigned short AreasBuf[2][IMG_WIDTH_MAX];


int dsp_SplitImage2AreasFast(
    unsigned char ** restrict Image,
    int Height, int Width,
    unsigned char *  restrict ModeNumb4Pix,
    unsigned short ** restrict Areas)
{
    //Изображение последовательно просматривается по строкам
    //для отнесения каждого пиксела к определённой области
    //Вычисление площади, занимаемой каждой областью и модой
    register int iAreasCounter;    //Счётчик количества областей в изображении
    register int iAreasInfoCounter;//Счётчик количества областей в контейнере
    //Временные переменные
    register int i, y, x, iSrcIndex, iDstIndex, iTmp;
    register int iCurrAreaNumb, iCurrModeNumb;
    register Uint32 id_LoadTransfer, id_SaveTransfer;
    register unsigned char * restrict pImageNextBuf;
    register unsigned char * restrict pImageCurrBuf;
    register unsigned short * restrict pAreasCurrBuf;
    register unsigned short * restrict pAreasPrevBuf;

    //Проверка корректности входных параметров
    if ((Image==0)||(ModeNumb4Pix==0)||(Areas==0)||
        (Width<=0)||(Height<=0)||(Width>IMG_WIDTH_MAX)) return -1;

    //Установка указателя на следующую строку изображения
    pImageNextBuf=ImageBuf[0];
    //Загрузка первой строки изображения
    id_LoadTransfer=DAT_copy(Image[0], pImageNextBuf, Width);
    //Начальная инициализация идентификатора канала сохранения
    id_SaveTransfer=DAT_XFRID_WAITNONE;

    //Обнуление счётчиков кол-ва областей
    iAreasCounter=iAreasInfoCounter=0;
    //Установка кол-ва областей максимально возможное для исключения
    //применения к первой строке ветви выявления связей с предыдущими строками
    iTmp=IMG_WIDTH_MAX;
    //Обнуление указателя на текущую строку
    pAreasCurrBuf=NULL;
    //Цикл по всем точкам изображения
    for (y=0; y<Height; y++)
    {
        //Сохранение указателя на текущую строку изображения
        pImageCurrBuf=pImageNextBuf;
        //Сохранение указателя на предыдущую строку результата
        pAreasPrevBuf=pAreasCurrBuf;
        //Указатель на следующую строку изображения
        pImageNextBuf=ImageBuf[(y+1)&1];
        //Указатель на текущую строку результата
        pAreasCurrBuf=AreasBuf[y&1];
        //Начальные значения номера моды
        iCurrModeNumb=-1;
        //Ожидание окончания загрузки текущей строки изображения
        DAT_wait(id_LoadTransfer);
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer = DAT_copy(Image[y+1], pImageNextBuf, Width);
        //Обработка очередной строки изображения
        for (x=0; x<Width; x++)
        {
            //Номер моды текущей точки
            i=ModeNumb4Pix[pImageCurrBuf[x]];
            //В зависимости от того принадлежит ли точка той же моде,
            //что и её левая точка, установка номера области
            if (iCurrModeNumb!=i)
            {
                //Добавление новой области
//                if (iAreasInfoCount>=MAX_AREAS) return -1;
                iCurrModeNumb=i;
                iCurrAreaNumb=iAreasInfoCounter;
                AreasMode[iAreasInfoCounter]=iCurrModeNumb;
                AreasNumber[iAreasInfoCounter]=iAreasInfoCounter;
                AreasPixel[iAreasInfoCounter]=x;
                //Инкремент счётчиков областей
                iAreasCounter++; iAreasInfoCounter++;
            }
            //Установка номера области текущей точки
            pAreasCurrBuf[x]=iCurrAreaNumb;
        }
        //Обработка всех областей в текущей строке для выявления их связей
        for (; iTmp<iAreasInfoCounter; iTmp++)
        {
            iSrcIndex=iTmp;
            iCurrModeNumb=AreasMode[iTmp];
            i=AreasPixel[iTmp];
            if (i>0) i--;
            if (iTmp==(iAreasInfoCounter-1))
                iCurrAreaNumb=Width-1;
            else
                iCurrAreaNumb=AreasPixel[iTmp+1];
            iCurrAreaNumb=pAreasPrevBuf[iCurrAreaNumb];
            //Цикл по всем граничащим на предыдущей строке областям
            for (i=pAreasPrevBuf[i]; i<=iCurrAreaNumb; i++)
            {
                if (AreasMode[i]!=iCurrModeNumb) continue;
                //На предыдущей строке найдена область с совпадающей модой,
                //которая соседствует с исходной
                iDstIndex=i;
                while(AreasNumber[iDstIndex]!=iDstIndex) iDstIndex=AreasNumber[iDstIndex];
                if (iSrcIndex!=iDstIndex)
                {
                    //Объединение двух кластеров областей
                    if (iSrcIndex<iDstIndex)
                    {
                        //Сохранение номера
                        AreasNumber[iDstIndex]=iSrcIndex;
                    }
                    else
                    {
                        //Сохранение номера
                        AreasNumber[iSrcIndex]=iDstIndex;
                        //Для дальнейшего объединения берётся меньший индекс
                        iSrcIndex=iDstIndex;
                    }
                    //Декремент кол-ва областей
                    iAreasCounter--;
                }
            }
        }
        //Сохранение кол-ва областей
        iTmp=iAreasInfoCounter;
        //Ожидание окончания сохранения результата
        DAT_wait(id_SaveTransfer);
        //Сохранение строки результата
        id_SaveTransfer=DAT_copy(pAreasCurrBuf, Areas[y], sizeof(short)*Width);
    }

    //Обновление ссылок на области
    for (iSrcIndex=0; iSrcIndex<iAreasInfoCounter; iSrcIndex++)
    {
        //Поиск индекса назначения
        iDstIndex=iSrcIndex;
        while(AreasNumber[iDstIndex]!=iDstIndex) iDstIndex=AreasNumber[iDstIndex];
        //Обновление номера области
        AreasNumber[iSrcIndex]=iDstIndex;
    }

    //Могут оставаться независимые области с индексами больше iAreasCount
    for (iDstIndex=-1, iSrcIndex=iAreasCounter; iSrcIndex<iAreasInfoCounter; iSrcIndex++)
    {
        //Если эта область уже переадресована - все нормально
        if (AreasNumber[iSrcIndex]!=iSrcIndex) continue;
        //Поиск индекса для переадресации
        for (iDstIndex++; iDstIndex<iAreasCounter; iDstIndex++)
            if (AreasNumber[iDstIndex]!=iDstIndex) break;
        //Сохранение указателя на моду
        AreasMode[iDstIndex]=AreasMode[iSrcIndex];
        //Установка ссылки данной области на переадресованную
        AreasNumber[iSrcIndex]=iDstIndex;
        //Замена всех ссылок на эту область!!!!!!!!
        for (i=iSrcIndex; i<iAreasInfoCounter; i++)
            if (AreasNumber[i]==iSrcIndex) AreasNumber[i]=iDstIndex;
    }

    //Установка указателя на текущую загружаемую строку
    pAreasCurrBuf=AreasBuf[0];
    //Загрузка первой строки номеров областей
    id_LoadTransfer=DAT_copy(Areas[0], pAreasCurrBuf, sizeof(short)*Width);
    //Начальная инициализация идентификатора канала сохранения
    id_SaveTransfer=DAT_XFRID_WAITNONE;
    //Обработка всех строк
    for (y=0; y<Height; y++)
    {
        //Сохранение указателя на предыдущую строку результата
        pAreasPrevBuf=pAreasCurrBuf;
        //Указатель на следующую строку результата
        pAreasCurrBuf=AreasBuf[(y+1)&1];
        //Ожидание окончания загрузки текущей строки изображения
        DAT_wait(id_LoadTransfer);
        //Запуск загрузки следующей строки изображения
        id_LoadTransfer=DAT_copy(Areas[y+1], pAreasCurrBuf, sizeof(short)*Width);
        //Переписывание индексов удалённых областей на переназначенные
        for (x=0; x<Width; x++) pAreasPrevBuf[x]=AreasNumber[pAreasPrevBuf[x]];
        //Ожидание окончания сохранения результата
        DAT_wait(id_SaveTransfer);
        //Сохранение строки результата
        id_SaveTransfer=DAT_copy(pAreasPrevBuf, Areas[y], sizeof(short)*Width);
    }
    //Ожидание окончания сохранения результата
    DAT_wait(id_SaveTransfer);

    return iAreasCounter;
}
SIA
Цитата(Degun @ Nov 7 2007, 22:59) *
Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера?

Глубина конвейера и их число - независимые величины. Кстати, глубина конвейера часто зависит от выполняемой операции.
Есть очень хорошая книжка, где все это расписано - П.М. Коуги, "Архитектура конвейерных ЭВМ".
Грубо говоря, длина конвейера - это сколько тактов от выдачи команды до получения результата данной команды (в то же время в процессе исполнения могут находиться другие команды, если для них есть данные, если их нет - то компилятор и ставит NOP-ы).
Число конвейеров - это то, сколько независимых друг от друга наборов данных могут обрабатываться параллельно. Естественно, все это хорошо работает только на задачах типа фильтрации, где нет зависимостей по данным (результат предыдущей операции для непосредственно следующей за ней не нужен).
Производительность таких систем радикально зависит от алгоритма, и для алгоритмов с большим числом ветвления (типа управляющих) нужен совершенно другой проц, с одним-двумя максимально короткими конвейерами и максимальной тактовой частотой, точнее, с максимальным отношением тактовой частоты к эффективной длине конвейера. Эффективная длина конвейера - это и есть число тактов после запуска операции до возможности использования результата в следующих командах или число потерянных тактов при переходе.
К примеру, эффективная длина конвейера процессоров MIPS на целочисленных операциях нормальной разрядности не превышает 2 (обычно 1), на плавающих - 5 (редко 7). А тактовые частоты достаточно высоки, поэтому неудивительно, что на задачах типа описанной Вами он работает гораздо эффективнее DSP.
Это имеет большой коммерческий смысл - благодаря этому свойству была создана и "раскручена" компания, предложившая очень экономичные эффективные решения в области security video, причем именно на MIPS, благодаря его высокой эффективности на реальном коде.

p.s. IMHO, любую задачу надо начинать с анализа алгоритмов, их написания и отладки на том же PC, и только после этого выбирать вычислительную платформу, а не просто смотреть на рекламные цифры. Иначе можно сильно погореть.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.