/// <summary> /// Realiza a identificação dos pontos na imagem que possuem correlação com a matriz de amostra. /// </summary> /// <param name="sample">Matriz de amostra.</param> /// <param name="bmpData">Um array contendo os valores R, G, B para os pixels do bitmap.</param> /// <param name="minFactor">Fator de correlação mínimo para considerar semelhante.</param> /// <returns>Uma lista dos pontos onde ocorreu a semelhança.</returns> /// <remarks>O tamanho da amostra deve ser um número ímpar.</remarks> public static List <PointCorrelation> TemplateMatching(ref byte[, ,] sample, ref byte[, ,] bmpData, double minFactor) { // tamanho do bitmap int width = bmpData.GetUpperBound(0) + 1; int height = bmpData.GetUpperBound(1) + 1; // tamanho da amostra int sampleSize = sample.GetUpperBound(0) + 1; // pontos candidatos List <PointCorrelation> candidatePointList = new List <PointCorrelation>(); // pontos encontrados List <PointCorrelation> pointList = new List <PointCorrelation>(); // valor da correlação double corrFactor = 0.0; // tamanho da matriz semente dos pontos candidatos if (sampleSize >= 9) { int seedSize = sampleSize / 4; if (seedSize % 2 == 0) { seedSize++; } // matriz de busca byte[, ,] seedSample = new byte[seedSize, seedSize, 3]; byte[, ,] seedSearch = new byte[seedSize, seedSize, 3]; // metade do tamanho do pixel semente int size = seedSize / 2; // cria matriz de amostra for (int i = sampleSize / 2 - size; i <= sampleSize / 2 + size; ++i) { for (int j = sampleSize / 2 - size; j <= sampleSize / 2 + size; ++j) { seedSample[i - sampleSize / 2 + size, j - sampleSize / 2 + size, 0] = sample[i, j, 0]; seedSample[i - sampleSize / 2 + size, j - sampleSize / 2 + size, 1] = sample[i, j, 1]; seedSample[i - sampleSize / 2 + size, j - sampleSize / 2 + size, 2] = sample[i, j, 2]; } } // busca os pontos candidatos for (int j = size; j < height - size; ++j) { for (int i = size; i < width - size; ++i) { // verifica se o ponto central da matriz de busca é branco (melhor performance) if (bmpData[i, j, 0] == 0) { continue; } // popula matriz de busca for (int l = j - size; l <= j + size; ++l) { for (int k = i - size; k <= i + size; ++k) { seedSearch[k - i + size, l - j + size, 0] = bmpData[k, l, 0]; seedSearch[k - i + size, l - j + size, 1] = bmpData[k, l, 1]; seedSearch[k - i + size, l - j + size, 2] = bmpData[k, l, 2]; } } // calcula correlação corrFactor = BitmapTools.CorrelationFactor(ref seedSample, ref seedSearch); if (corrFactor >= 0) { candidatePointList.Add(new PointCorrelation(i, j, corrFactor)); } } } // metade do tamanho do pixel semente size = sampleSize / 2; // matriz de busca byte[, ,] search = new byte[sampleSize, sampleSize, 3]; // verifica se os pontos candidatos podem ser considerados foreach (PointCorrelation point in candidatePointList) { if ((point.X - size) < 0 || (point.Y - size < 0) || (point.X + size >= width) || (point.Y + size >= height)) { continue; } // popula matriz de busca for (int l = point.Y - size; l <= point.Y + size; ++l) { for (int k = point.X - size; k <= point.X + size; ++k) { search[k - point.X + size, l - point.Y + size, 0] = bmpData[k, l, 0]; search[k - point.X + size, l - point.Y + size, 1] = bmpData[k, l, 1]; search[k - point.X + size, l - point.Y + size, 2] = bmpData[k, l, 2]; } } // calcula correlação corrFactor = BitmapTools.CorrelationFactor(ref sample, ref search); if (corrFactor >= minFactor) { // verifica se já existe um ponto na lista com a distância menor que sampleSize bool found = false; foreach (PointCorrelation p in pointList) { if (Math.Sqrt((p.X - point.X) * (p.X - point.X) + (p.Y - point.Y) * (p.Y - point.Y)) <= sampleSize) { if (corrFactor >= p.CorrelationValue) { pointList.Remove(p); pointList.Add(new PointCorrelation(point.X, point.Y, corrFactor)); } found = true; break; } } // adiciona o ponto na lista if (!found) { pointList.Add(new PointCorrelation(point.X, point.Y, corrFactor)); } } } } else { // matriz de busca byte[, ,] search = new byte[sampleSize, sampleSize, 3]; // metade do tamanho do pixel semente int size = sampleSize / 2; // busca os pontos for (int j = size; j < height - size; ++j) { for (int i = size; i < width - size; ++i) { // popula matriz de busca for (int l = j - size; l <= j + size; ++l) { for (int k = i - size; k <= i + size; ++k) { search[k - i + size, l - j + size, 0] = bmpData[k, l, 0]; search[k - i + size, l - j + size, 1] = bmpData[k, l, 1]; search[k - i + size, l - j + size, 2] = bmpData[k, l, 2]; } } // calcula correlação corrFactor = BitmapTools.CorrelationFactor(ref sample, ref search); if (corrFactor >= minFactor) { // verifica se já existe um ponto na lista com a distância menor que sampleSize bool found = false; foreach (PointCorrelation p in pointList) { if (Math.Sqrt((p.X - i) * (p.X - i) + (p.Y - j) * (p.Y - j)) <= sampleSize) { if (corrFactor >= p.CorrelationValue) { pointList.Remove(p); pointList.Add(new PointCorrelation(i, j, corrFactor)); } found = true; break; } } // adiciona o ponto na lista if (!found) { pointList.Add(new PointCorrelation(i, j, corrFactor)); } } } } } // retorna a lista de pontos return(pointList); }
/// <summary> /// Procura na imagem por regiões que representam pontos de referência. /// </summary> /// <param name="bmpData">Um array contendo os valores R, G, B para os pixels do bitmap.</param> /// <param name="minFactor">Fator de correlação mínimo para considerar semelhante duas matrizes (-1 a 1).</param> /// <returns>Uma lista com a coordenada e valor de correlação dos pontos.</returns> /// <remarks>Este método considera que a imagem seja preto e branco, sendo que a cor branca representa /// as regiões e a cor preta o plano de fundo.</remarks> public static List <PointCorrelation> FindImagePoints(ref byte[, ,] bmpData, double minFactor) { List <PointCorrelation> pointList = null; // ponto semente Point seedPoint = new Point(0, 0); bool findSeed = false; // tamanho do bitmap int width = bmpData.GetUpperBound(0) + 1; int height = bmpData.GetUpperBound(1) + 1; //// procura pelo ponto semente //// obs.: Inicia de baixo pra cima. //for (int i = 0; i < width; ++i) //{ // for (int j = height - 1; j >= 0; --j) // { // if (bmpData[i, j, 0] == 255) // { // seedPoint.X = i; // seedPoint.Y = j; // // flag // findSeed = true; // // sai do loop // j = -1; // i = width; // } // } //} // procura pelo ponto semente // obs.: Inicia da metade da imagem e segue crescendo Y. for (int j = height / 2; j < height; ++j) { for (int i = 0; i < width; ++i) { if (bmpData[i, j, 0] == 255) { // verifica se os vizinhos do ponto são brancos também if (i >= 2 && i <= (width - 2) && j <= (height - 2)) { if (bmpData[i - 1, j - 1, 0] == 255 && bmpData[i - 1, j, 0] == 255 && bmpData[i - 1, j + 1, 0] == 255 && bmpData[i, j - 1, 0] == 255 && bmpData[i, j + 1, 0] == 255 && bmpData[i + 1, j - 1, 0] == 255 && bmpData[i + 1, j, 0] == 255 && bmpData[i + 1, j + 1, 0] == 255) { seedPoint.X = i; seedPoint.Y = j; // flag findSeed = true; // sai do loop j = height; i = width; } } } } } // se não encontrou o ponto, inicia da origem da imagem e vai até a metade. if (!findSeed) { for (int j = 0; j < height / 2; ++j) { for (int i = 0; i < width; ++i) { if (bmpData[i, j, 0] == 255) { // verifica se os vizinhos do ponto são brancos também if (i >= 2 && i <= (width - 2) && j >= 2) { if (bmpData[i - 1, j - 1, 0] == 255 && bmpData[i - 1, j, 0] == 255 && bmpData[i - 1, j + 1, 0] == 255 && bmpData[i, j - 1, 0] == 255 && bmpData[i, j + 1, 0] == 255 && bmpData[i + 1, j - 1, 0] == 255 && bmpData[i + 1, j, 0] == 255 && bmpData[i + 1, j + 1, 0] == 255) { seedPoint.X = i; seedPoint.Y = j; // flag findSeed = true; // sai do loop j = height; i = width; } } } } } } // verifica se encontrou o ponto semente // se não encontrou, retorna uma lista vazia de pontos if (!findSeed) { return(new List <PointCorrelation>()); } // pilha contendo pontos a serem verificados Stack <Point> pointStack = new Stack <Point>(); // cria array contendo flags para identificar a região bool[,] regionFlags = new bool[width, height]; // inicializa array for (int i = 0; i < width; ++i) { for (int j = 0; j < height; ++j) { regionFlags[i, j] = false; } } // indica que o ponto semente pertence à região regionFlags[seedPoint.X, seedPoint.Y] = true; // adiciona ponto na pilha de pontos pointStack.Push(seedPoint); // coordenadas corrente int x = 0; int y = 0; while (pointStack.Count != 0) { // remove ponto Point p = pointStack.Pop(); x = p.X; y = p.Y; // vizinhos da esquerda if (x - 1 >= 0) { // esquerda if (bmpData[x - 1, y, 0] == 255) { if (!regionFlags[x - 1, y]) { regionFlags[x - 1, y] = true; pointStack.Push(new Point(x - 1, y)); } } // esquerda superior if (y - 1 >= 0) { if (bmpData[x - 1, y - 1, 0] == 255) { if (!regionFlags[x - 1, y - 1]) { regionFlags[x - 1, y - 1] = true; pointStack.Push(new Point(x - 1, y - 1)); } } } // esquerda inferior if (y + 1 < height) { if (bmpData[x - 1, y + 1, 0] == 255) { if (!regionFlags[x - 1, y + 1]) { regionFlags[x - 1, y + 1] = true; pointStack.Push(new Point(x - 1, y + 1)); } } } } // vizinho superior if (y - 1 >= 0) { if (bmpData[x, y - 1, 0] == 255) { if (!regionFlags[x, y - 1]) { regionFlags[x, y - 1] = true; pointStack.Push(new Point(x, y - 1)); } } } // vizinho inferior if (y + 1 < height) { if (bmpData[x, y + 1, 0] == 255) { if (!regionFlags[x, y + 1]) { regionFlags[x, y + 1] = true; pointStack.Push(new Point(x, y + 1)); } } } // vizinhos à direita if (x + 1 < width) { // direita if (bmpData[x + 1, y, 0] == 255) { if (!regionFlags[x + 1, y]) { regionFlags[x + 1, y] = true; pointStack.Push(new Point(x + 1, y)); } } // direita superior if (y - 1 >= 0) { if (bmpData[x + 1, y - 1, 0] == 255) { if (!regionFlags[x + 1, y - 1]) { regionFlags[x + 1, y - 1] = true; pointStack.Push(new Point(x + 1, y - 1)); } } } // direita inferior if (y + 1 < height) { if (bmpData[x + 1, y + 1, 0] == 255) { if (!regionFlags[x + 1, y + 1]) { regionFlags[x + 1, y + 1] = true; pointStack.Push(new Point(x + 1, y + 1)); } } } } } int xmin = width - 1, ymin = height - 1; int xmax = 0, ymax = 0; // procura pelos pontos mínimo e máximo da região for (int i = 0; i < width; ++i) { for (int j = 0; j < height; ++j) { if (regionFlags[i, j]) { if (i < xmin) { xmin = i; } if (i > xmax) { xmax = i; } if (j < ymin) { ymin = j; } if (j > ymax) { ymax = j; } } } } // tamanho da matriz de amostra int sampleSize = (int)Math.Max(xmax - xmin, ymax - ymin); // verifica o tamanho do ponto semente if (sampleSize < 2) { return(new List <PointCorrelation>()); } // o tamanho deve ser um número ímpar if (sampleSize % 2 == 0) { sampleSize++; } // cria matriz de amostra byte[, ,] sample = new byte[sampleSize, sampleSize, 3]; // corrige ponto semente seedPoint.X = xmin + ((xmax - xmin) / 2); seedPoint.Y = ymin + ((ymax - ymin) / 2); // metade do tamanho da matriz de amosta int size = sampleSize / 2; // copia valores para a matriz de amostra for (int i = seedPoint.X - size; i <= seedPoint.X + size; ++i) { for (int j = seedPoint.Y - size; j <= seedPoint.Y + size; ++j) { sample[i - seedPoint.X + size, j - seedPoint.Y + size, 0] = bmpData[i, j, 0]; sample[i - seedPoint.X + size, j - seedPoint.Y + size, 1] = bmpData[i, j, 1]; sample[i - seedPoint.X + size, j - seedPoint.Y + size, 2] = bmpData[i, j, 2]; } } // realiza template matching pointList = BitmapTools.TemplateMatching(ref sample, ref bmpData, minFactor); // retorna lista de pontos return(pointList); }