Dada una matriz 0-1, encuentre la comprensión de la recurrencia del programa de dominio conectado

Dada una matriz 0-1 para encontrar el dominio conectado blog-CSDN blog_connected del dominio_xunan003

El método de este artículo para encontrar un dominio conectado a la matriz 0-1 es relativamente detallado, pero debido a que nunca he aprendido C++ y tengo poca experiencia en programación, el programa que se proporciona en esta publicación de blog no es muy comprensible.Tian finalmente tiene algunas pistas. Haré un resumen aquí y lo compartiré con Xiaobai, espero que también pueda ser útil para usted.

El método para encontrar el dominio conectado se divide en tres pasos

El primer paso: (abrir el refrigerador) Para una matriz bidimensional, primero encuentre las coordenadas iniciales y finales (columnas) de cada fila de camarillas blancas, y asigne a cada camarilla una secuencia de etiquetas

Paso 2: fusionar las etiquetas de camarillas cercanas. La forma de identificar camarillas cercanas es a través de la superposición de trayectorias (columnas). Las trayectorias se superponen y las etiquetas se naturalizan. Si una camarilla está estrechamente relacionada con dos tipos diferentes de camarillas, significa que los dos tipos de camarillas son equivalentes (se utilizan dos casos como estándar en la etapa de selección inicial) y se requiere un aislamiento uniforme.

Paso 3: Resuma y evalúe los grupos equivalentes y clasifique todos los grupos superpuestos en una categoría, para dividirlos en diferentes grupos de infección.

Se dan tres procedimientos, correspondientes a los tres pasos anteriores respectivamente.

void fillRunVectors(const Mat& bwImage, int& NumberOfRuns, vector<int>& stRun, vector<int>& enRun, vector<int>& rowRun)

void firstPass(vector<int>& stRun, vector<int>& enRun, vector<int>& rowRun, int NumberOfRuns,
    vector<int>& runLabels, vector<pair<int, int>>& equivalences, int offset)

void replaceSameLabel(vector<int>& runLabels, vector<pair<int, int>>&
    equivalence)

Leer los pasos del programa

primer procedimiento

1. Comprender el propósito y la implementación del programa

Cuando obtenga cada programa, debe comprender completamente lo que hace y cómo se implementa. Si no hay una descripción detallada de cómo implementarlo, debe adivinar y verificar, de lo contrario, será muy doloroso leer el programa, solo como un artículo que no sabe las palabras Comprensión de lectura ininteligible.

El primer programa completa las coordenadas de inicio y final de cada fila de camarillas blancas y le da a cada camarilla una secuencia de marcas.

1. Cómo marcar las coordenadas

El punto j1 de la i-ésima fila es la coordenada inicial de la mancha blanca y el punto j2 es la coordenada final, por lo que j1 debe ser empujado a la cola de inicio, y j2 es empujado a la cola final, y correspondientemente pertenece a la i-ésima fila.

2. Cómo distinguir las coordenadas de inicio y fin

Punto de inicio (1, la primera columna son píxeles blancos; 2, esta columna son píxeles blancos y la columna de la izquierda son píxeles negros)

Punto de terminación (1, esta columna son píxeles blancos, la columna de la derecha son píxeles negros; 2, la última columna son píxeles blancos)

2. Comprender las funciones correspondientes a la estructura de grande a pequeña en el programa (estructura de grande a pequeña)

El cuerpo principal tiene dos bucles for, ¿qué es cada bucle for bucle?

recorro filas, recorro columnas, encuentro las coordenadas de los puntos inicial y final

¿Cuál es el juicio de la declaración condicional (ver el contenido de los paréntesis)? ¿Qué función se realiza después de la selección (ver la conclusión al final de las llaves)?

Determine si es un píxel blanco y presione el valor de la coordenada respectivamente

3. Según el comportamiento de las variables, se puede adivinar la referencia y el significado de cada variable correspondiente a la función objetivo

NumberofRuns representa el número de grupos blancos, y rowrun corresponde al número de filas a las que pertenece cada grupo blanco

strun representa las coordenadas iniciales de cada grupo blanco, enrun representa las coordenadas finales de cada grupo blanco

En cuarto lugar, el procedimiento de revisión integral final

void fillRunVectors(const Mat& bwImage, int& NumberOfRuns, vector<int>& stRun, vector<int>& enRun, vector<int>& rowRun)
{
    for (int i = 0; i < bwImage.rows; i++)
    {
        const uchar* rowData = bwImage.ptr<uchar>(i);

        if (rowData[0] == 255)
        {
            NumberOfRuns++;
            stRun.push_back(0);
            rowRun.push_back(i);
        }
        for (int j = 1; j < bwImage.cols; j++)
        {
            if (rowData[j - 1] == 0 && rowData[j] == 255)
            {
                NumberOfRuns++;
                stRun.push_back(j);
                rowRun.push_back(i);
            }
            else if (rowData[j - 1] == 255 && rowData[j] == 0)
            {
                enRun.push_back(j - 1);
            }
        }
        if (rowData[bwImage.cols - 1])
        {
            enRun.push_back(bwImage.cols - 1);
        }
    }
}

segundo procedimiento

Todavía siga los cuatro pasos anteriores

1. La función firstPass completa el marcado del grupo y la generación de la lista de pares equivalentes

a. Los grupos en la primera línea están etiquetados respectivamente;

b1 Para las camarillas en todas las filas excepto la primera fila, si no tiene un área superpuesta con todas las camarillas en la fila anterior, asígnele una nueva etiqueta;

b2 Si solo tiene un área de superposición con una camarilla en la fila anterior, asígnele la etiqueta de la camarilla en la fila anterior;

b3 Si dos o más camarillas en la fila anterior tienen áreas superpuestas, asigne la etiqueta mínima de una camarilla conectada a la camarilla actual y escriba las marcas de estas camarillas en la fila anterior en pares equivalentes, indicando que pertenecen a una categoría. (Un par de equivalencia está entre dos camarillas)

1. ¿Cómo juzgar que dos grupos en filas adyacentes se superponen?

A juzgar por la coincidencia del comienzo y el final de los dos regimientos, curstart>preend&curend<prestart

2. Todavía hay dos bucles for anidados

El ciclo for implementa la función de recorrido, y el objeto del ciclo se puede solicitar de acuerdo con el límite superior del ciclo.

Esta función necesita atravesar todos los grupos y leer las coordenadas de inicio y fin de cada grupo

Es necesario recorrer todas las camarillas de la fila anterior y leer las coordenadas de inicio y fin de cada camarilla de la fila anterior

Comparación de las dos informaciones para el juicio superpuesto

La primera oración condicional implementa la actualización y reemplazo del contenido recursivo (el contenido de la línea anterior se actualiza)

La segunda oración condicional implementa el juicio repetido y etiqueta los grupos para generar pares equivalentes.

3. Adivina audazmente y verifica cuidadosamente el significado de cada variable

Las coordenadas de inicio de la delegación strun, enrun las coordenadas finales, el número de filas correspondientes a la delegación rowrun, currowidx el número de filas juzgadas actualmente, la etiqueta de la delegación runlabels y el par equivalente almacenado en equivalencia

void firstPass(vector<int>& stRun, vector<int>& enRun, vector<int>& rowRun, int NumberOfRuns,
    vector<int>& runLabels, vector<pair<int, int>>& equivalences, int offset)
{
    runLabels.assign(NumberOfRuns, 0);
    int idxLabel = 1;
    int curRowIdx = 0;
    int firstRunOnCur = 0;
    int firstRunOnPre = 0;
    int lastRunOnPre = -1;
    for (int i = 0; i < NumberOfRuns; i++)
    {
        if (rowRun[i] != curRowIdx)
        {
            curRowIdx = rowRun[i];
            firstRunOnPre = firstRunOnCur;
            lastRunOnPre = i - 1;
            firstRunOnCur = i;

        }
        for (int j = firstRunOnPre; j <= lastRunOnPre; j++)
        {
            if (stRun[i] <= enRun[j] + offset && enRun[i] >= stRun[j] - offset && rowRun[i] == rowRun[j] + 1)
            {
                if (runLabels[i] == 0) // 没有被标号过
                    runLabels[i] = runLabels[j];
                else if (runLabels[i] != runLabels[j])// 已经被标号             
                    equivalences.push_back(make_pair(runLabels[i], runLabels[j])); // 保存等价对
            }
        }
        if (runLabels[i] == 0) // 没有与前一列的任何run重合
        {
            runLabels[i] = idxLabel++;
        }

    }
}

tercer procedimiento

1. Para tratar con pares de equivalencia, es necesario transformarlo en varias secuencias de equivalencia

Por ejemplo, existen los siguientes pares de equivalencia:

(1,2),(1,6),(3,7),(9-3),(8,1),(8,10),(11,5),(11,8),(11 ,12),(11,13),(11,14),(15,11)

Necesitamos obtener la secuencia final es:

1-2-5-6-8-10-11-12-13-14-15

3-7-9

4

El método utilizado es el principio de recorrido de la imagen primero en profundidad para buscar secuencias equivalentes.

No se apresure a leer el programa, primero comprenda el principio del método , de lo contrario, será muy doloroso leer el programa, parece que ya comenzó, pero de hecho, no puede leer el programa palabra por palabra. (como comprensión de lectura en inglés), lea el programa de acuerdo con la estructura y función.

Principio transversal de la profundidad de la imagen primero

Encuentre el grupo W equivalente (vinculado) de todos los grupos V y el siguiente punto adyacente de V relativo a W.

referencias:

Recorrido de gráficos: recorrido primero en profundidad (DFS)_Uncertainty!!'s blog-CSDN blog_recorrido primero en profundidad

En segundo lugar, de acuerdo con el principio de recorrido primero en profundidad, el proceso de encontrar dominios conectados requiere varios procesos transversales anidados.

Primero, debe recorrer todas las camarillas (columnas de la matriz de arriba a abajo); debe atravesar las camarillas equivalentes a una determinada camarilla (filas de la matriz de izquierda a derecha); también necesita atravesar las camarillas equivalentes adyacentes a esta camarilla equivalente ( columnas de matriz de arriba a abajo)

Correspondientes respectivamente a i<maxlabel (número de grupo), j<tempList (secuencia equivalente temporal), k<eqTab[].size()=maxLabel

3. Adivina el significado de cada variable

maxLabel representa el número de gráficos, eqTab representa la matriz de pares equivalente, vecPairIt representa el circulador, y su función es recorrer todo el eqTab

labelFlag representa la clase a la que pertenece, tempList representa la secuencia equivalente de una clase temporal y equaList representa la secuencia equivalente final

void replaceSameLabel(vector<int>& runLabels, vector<pair<int, int>>&
    equivalence)
{
    int maxLabel = *max_element(runLabels.begin(), runLabels.end());
    vector<vector<bool>> eqTab(maxLabel, vector<bool>(maxLabel, false));
    vector<pair<int, int>>::iterator vecPairIt = equivalence.begin();
    while (vecPairIt != equivalence.end())
    {
        eqTab[vecPairIt->first - 1][vecPairIt->second - 1] = true;
        eqTab[vecPairIt->second - 1][vecPairIt->first - 1] = true;
        vecPairIt++;
    }
    vector<int> labelFlag(maxLabel, 0);
    vector<vector<int>> equaList;
    vector<int> tempList;
    cout << maxLabel << endl;
    for (int i = 1; i <= maxLabel; i++)
    {
        if (labelFlag[i - 1])
        {
            continue;
        }
        labelFlag[i - 1] = equaList.size() + 1;
        tempList.push_back(i);
        for (vector<int>::size_type j = 0; j < tempList.size(); j++)
        {
            for (vector<bool>::size_type k = 0; k != eqTab[tempList[j] - 1].size(); k++)
            {
                if (eqTab[tempList[j] - 1][k] && !labelFlag[k])
                {
                    tempList.push_back(k + 1);
                    labelFlag[k] = equaList.size() + 1;
                }
            }
        }
        equaList.push_back(tempList);
        tempList.clear();
    }
    cout << equaList.size() << endl;
    for (vector<int>::size_type i = 0; i != runLabels.size(); i++)
    {
        runLabels[i] = labelFlag[runLabels[i] - 1];
    }
}

PD: algunos pequeños registros de problemas para la comprensión del programa

1. si (a), si a es verdadero, continúe, donde verdadero incluye números y caracteres distintos de cero.

2. vector<bool>(maxLabel, false)  std :: vector <bool> a(b,false); Buscar en C# -789 (789zhao.com)

3. iterador vecPairIt  C++ iterador iterador explicación detallada_lenguaje C_Script House (jb51.net)

4. vecPairIt->  (1 mensaje) -> Diálisis en el blog de C_Fan Hongkang-CSDN Blog

5. eqTab[vecPairIt->primero - 1][vecPairIt->segundo - 1] (1 mensaje) El iterador del contenedor de mapa en c++ primero segundo blog_imik-CSDN blog_mapa primero y segundo

6. Vector<vector<bool>>  Use vector para realizar vectores bidimensionales - Biblioteca Baidu (baidu.com)

7. "&&", "||" y "!". a && b, uno falso debe ser falso, asociatividad de izquierda a derecha. || es un operador lógico u, a || b, un verdadero debe ser verdadero, asociatividad de izquierda a derecha, &&! Se establece solo si se establece el lado izquierdo de && y no se establece el lado derecho;

 Al final, por qué sientes que un programa tan pequeño es tan difícil, 5555, comencé a dudar si es adecuado para hacer algoritmos de imagen, ¡vamos, mono!

Finalmente, gracias por su ayuda en el proceso de resolver el problema. Si mi respuesta le ayuda, por favor deme un pulgar hacia arriba ~~ 

Supongo que te gusta

Origin blog.csdn.net/MOZHOUH/article/details/125009011
Recomendado
Clasificación