基于图的图像分割(Graph-BasedImageSegmentation)的多线程分块与合并

84 篇文章 4 订阅

代码是基于上一篇文章基于图的图像分割(Graph-BasedImageSegmentation)的分块实现的修改,主要是增加多线程,以及分块之后结果的合并的功能,代码相对复杂,因为关联了其他源文件,所以代码仅供参考。

先上头文件:

#pragma once

#ifdef DLLProvider
#define DLL_API_C extern "C" __declspec(dllexport)
#define DLL_API __declspec(dllexport)
#else
#define DLL_API_C extern "C" __declspec(dllimport)
#define DLL_API __declspec(dllimport)
#endif

//nBgValue = 0/255; block_size = 2048; sigma = 0.0; k = 0.0; min_size = 100;
int ImgSegBlock(unsigned char* pDataSrc, int width, int height, int nBgValue, unsigned char* pDataDst, int block_size, float sigma, float k, int min_size);
//只能接受8位3通道输入,返回值为分块数目,输出连通域的索引从1开始
int ImgSegBlockMark(unsigned char* pDataSrc, int width, int height, int nBgValue, unsigned int* pDataDst, int block_size, float sigma, float k, int min_size);
//使用K均值将得到的碎块进行合并
int KMeansSegment(unsigned char* pDataSrc, int width, int height, int nBgValue, unsigned int* pDataDst, int block_size, float sigma, float k, int min_size);

然后是cpp文件:

/*
Copyright (C) 2006 Pedro Felzenszwalb

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

#include <cstdio>
#include <cstdlib>
#include <image.h>
#include <misc.h>
#include <pnmfile.h>
#include "segment-image.h"
#include <map>
#include <omp.h>

//#define DLLProvider
#include "imgseg.h"
#include "..\FindBoundary.h"
#include "arealines.h"
#include "k-means.h"
//#include "Log.h"
#include "..\src\StdStrFile.h"

//参数范围
#define SIGMA_MIN	0.1
#define SIGMA_MAX	0.5
#define K_MIN		5
#define K_MAX		100

#ifndef MIN
#  define MIN(a,b)  ((a) > (b) ? (b) : (a))
#endif

#ifndef MAX
#  define MAX(a,b)  ((a) < (b) ? (b) : (a))
#endif

#define imData(pData, x, y, width) (pData[(y) * (width) + x])

void inline CheckParameters(int width, int height, float& sigma, float& k)
{
	if (sigma < 1e-5)
	{
		//根据宽高自动设置sigma的值
		//sigma = MAX(SIGMA_MIN, MIN(SIGMA_MAX, MAX(width, height) / 10000.0));
		sigma = 0.0;
	}

	if (k < 1e-5)
	{
		//根据宽高自动设置k的值
		//k = MAX(K_MIN, MIN(K_MAX, MAX(width, height) / 50.0));
		k = 0.0;
	}
}

//调试用
void inline SaveBufferPPM(unsigned char* pDataSrc, int width, int height, const char* szFilePath = "D:\\a.ppm")
{
	image<rgb> *input = new image<rgb>(width, height);
	memcpy(input->data, pDataSrc, width * height * 3);
	savePPM(input, szFilePath);
	delete input;
}

void inline SaveBoundBufferPPM(unsigned char* pDataSrc, int width, int height, const char* szFilePath = "D:\\b.ppm")
{
	image<rgb> *input = new image<rgb>(width, height);
	for (size_t j = 0; j < height; ++j)
	{
		unsigned char* pSubData = pDataSrc + j * width;
		rgb* data = input->data + j * width;
		for (size_t i = 0; i < width; ++i)
		{
			if (pSubData[i])
			{
				data[i].r  = data[i].g =data[i].b = 255;
			}
			else
			{
				data[i].r  = data[i].g =data[i].b = 0;
			}
		}
	}
	savePPM(input, szFilePath);
	delete input;
}

void inline SaveBufferPPM(unsigned int* pDataSrc, int width, int height, const char* szFilePath = "D:\\seg.ppm")
{
	// pick random colors for each component
	rgb *colors = new rgb[width*height]();
	for (int i = 0; i < width*height; i++)
		colors[i] = random_rgb();

	image<rgb> *input = new image<rgb>(width, height);
	for (size_t j = 0; j < height; ++j)
	{
		unsigned int* pInt = pDataSrc + j * width;
		rgb* data = input->data + j * width;
		for (size_t i = 0; i < width; ++i)
		{
			data[i] = colors[pInt[i]];
		}
	}
	savePPM(input, szFilePath);
	delete input;
}

int SegmentPPM(const char* szFilePath, float sigma, float k, int min_size, const char* szDstPath)
{
	if (szFilePath == nullptr || szDstPath == nullptr)
	{
		return -1;
	}

	printf("loading input image.\n");
	image<rgb> *input = loadPPM(szFilePath);

	int width = input->width();
	int height = input->height();
	CheckParameters(width, height, sigma, k);

	printf("processing\n");
	int num_ccs = 0; 
	uchar* pBound = new uchar[width * height]();
	image<rgb> *seg = segment_image(input, sigma, k, min_size, &num_ccs, pBound); 
	delete[] pBound;
	savePPM(seg, szDstPath);
	delete seg;
	printf("got %d components\n", num_ccs);
	printf("done! uff...thats hard work.\n");

	return 0;
}

int ImgSegmentBlock(unsigned char* pDataSrc, int width, int height, uchar* pBound, unsigned char* pDataDst, int block_size, float sigma, float k, int min_size)
{
	CheckParameters(width, height, sigma, k);

	int num_ccs = 0;
	if (block_size <= 0 || width <= block_size && height <= block_size)
	{
		image<rgb> *input = new image<rgb>(width, height);
		memcpy(input->data, pDataSrc, width * height * 3);
		printf("processing\n");
		image<rgb> *seg = segment_image(input, sigma, k, min_size, &num_ccs, pBound); 
		memcpy(pDataDst, seg->data, width * height * 3);
		delete input;
		delete seg;
		printf("got %d components\n", num_ccs);
		printf("done! uff...thats hard work.\n");
	}
	else
	{
		//分块处理当前数据块,最后一行和最后一列要合并到上一块进行处理
		int nColIndex = MAX(1, (width /*- 1*/) / block_size /*+ 1*/);		//计算列方向上块数
		int nRowIndex = MAX(1, (height /*- 1*/) / block_size /*+ 1*/);		//计算行方向块数
		//定义核心数个buffer
		int nThreadNum = omp_get_num_procs();
		int nBISum = nColIndex * nRowIndex;
#pragma omp parallel for num_threads(nThreadNum) reduction(+:num_ccs)
		for (int nBI = 0; nBI < nBISum; nBI++)
		{
			int nRI = nBI / nColIndex;
			int nCI = nBI % nColIndex;
			unsigned char* pRDataSrc = pDataSrc + nRI * block_size * width * 3;
			unsigned char* pRDataDst = pDataDst + nRI * block_size * width * 3;
			uchar* pRDataBound = pBound + nRI * block_size * width;

			int nCurTh = nBI % nThreadNum;
			int nBlockWidth = block_size;
			int nBlockHeight = block_size;
			//行列末尾块处理
			if (nCI == nColIndex - 1 && width % block_size != 0)nBlockWidth = (width) % block_size + block_size;
			if (nRI == nRowIndex - 1 && height % block_size != 0)nBlockHeight = (height) % block_size + block_size;
			nBlockWidth = MIN(nBlockWidth, width);
			nBlockHeight = MIN(nBlockHeight, height);
			//每个线程独立管理内存
			unsigned char*	pDataBlockSrc = new unsigned char[nBlockWidth * nBlockHeight * 3]();
			unsigned char*	pDataBlockDst = new unsigned char[nBlockWidth * nBlockHeight * 3]();
			unsigned char*	pDataBound = new uchar[nBlockWidth * nBlockHeight]();
			//输出缓存必须清零
			//memset(pDataBlockSrc, 0, nBlockWidth * nBlockHeight * 3);
			//memset(pDataBlockDst, 0, nBlockWidth * nBlockHeight * 3);
			//memset(pDataBound, 0, nBlockWidth * nBlockHeight);
			//得到每一块的数据
			unsigned char* pCDataSrc = pRDataSrc + nCI * block_size * 3;
			unsigned char* pCDataDst = pRDataDst + nCI * block_size * 3;
			uchar* pCDataBound = pRDataBound + nCI * block_size;

			for (size_t j = 0; j < nBlockHeight; ++j)
			{
				unsigned char* pSubDataSrc = pCDataSrc + j * width * 3;
				unsigned char* pSubBlockDataDst = pDataBlockSrc + j * nBlockWidth * 3;
				uchar* pSubDataBoundSrc = pCDataBound + j * width;
				uchar* pSubDataBoundDst = pDataBound + j * nBlockWidth;
				memcpy(pSubBlockDataDst, pSubDataSrc, nBlockWidth * 3);
				memcpy(pSubDataBoundDst, pSubDataBoundSrc, nBlockWidth);
			}

			num_ccs += ImgSegmentBlock(pDataBlockSrc, nBlockWidth, nBlockHeight, pDataBound, pDataBlockDst, -1, sigma, k, min_size);
			//将目标结果覆盖到当前区域
			for (size_t j = 0; j < nBlockHeight; ++j)
			{
				unsigned char* pSubDataDst = pCDataDst + j * width * 3;
				unsigned char* pSubBlockDataDst = pDataBlockDst + j * nBlockWidth * 3;
				memcpy(pSubDataDst, pSubBlockDataDst, nBlockWidth * 3);
			}

			delete[] pDataBlockSrc;
			delete[] pDataBlockDst;
			delete[] pDataBound;
		}
	}

	return num_ccs;
}

int ImgSegmentBlockMark(unsigned char* pDataSrc, int width, int height, uchar* pBound,
	unsigned int* pDataDst, int block_size, int nLastIndex, float sigma, float k, int min_size)
{
	CheckParameters(width, height, sigma, k);

	if (block_size <= 0 || width <= block_size && height <= block_size)
	{
		image<rgb> *input = new image<rgb>(width, height);
		memcpy(input->data, pDataSrc, width * height * 3);
		printf("processing\n");

		universe* u = segment_image_u(input, sigma, k, min_size, pBound);

		int num_ccs = u->num_sets();
		//输出内存必须覆盖,因为边界部分不会赋值
		memset(pDataDst, 0, width * height * sizeof(int));
		std::map<int, int> mvs;
		for (size_t j = 0; j < height; ++j)
		{
			unsigned int* data = pDataDst + j * width;
			uchar* bound = pBound + j * width;
			for (size_t i = 0; i < width; ++i)
			{
				if (!bound[i])
				{
					int comp = u->find(j * width + i);
					if (mvs.find(comp) == mvs.end())
					{
						mvs.insert(std::make_pair(comp, ++nLastIndex));
					}
					data[i] = mvs[comp];
				}
				else
				{
					--num_ccs;
				}
			}
		}

		printf("got %d components\n", num_ccs);
		printf("done! uff...thats hard work.\n");

		delete input;
		delete u;

		return num_ccs;
	}
	else
	{
		//分块处理当前数据块
		int nColIndex = MAX(1, (width /*- 1*/) / block_size /*+ 1*/);		//计算列方向上块数
		int nRowIndex = MAX(1, (height /*- 1*/) / block_size /*+ 1*/);		//计算行方向块数

		//定义核心数个buffer
		int nThreadNum = omp_get_num_procs();

		int nBlockWidthB = block_size * 2;						//当前块宽度
		int nBlockHeightB = block_size * 2;						//当前块高度
		unsigned char** pDataBlock = new unsigned char*[nThreadNum]();
		unsigned int** pDataBlockDst = new unsigned int*[nThreadNum]();
		uchar** pDataBound = new uchar*[nThreadNum]();
		for (int i = 0; i < nThreadNum; ++i)
		{
			pDataBlock[i] = new unsigned char[nBlockWidthB * nBlockHeightB * 3]();
			pDataBlockDst[i] = new unsigned int[nBlockWidthB * nBlockHeightB]();
			pDataBound[i] = new uchar[nBlockWidthB * nBlockHeightB]();
		}

		int nBISum = nColIndex * nRowIndex;
		int* nCompCount = new int[nBISum]();
#pragma omp parallel for num_threads(nThreadNum)
		for (int nBI = 0; nBI < nBISum; nBI++)
		{
			int nRI = nBI / nColIndex;
			int nCI = nBI % nColIndex;
			unsigned char* pRDataSrc = pDataSrc + nRI * block_size * width * 3;
			unsigned int* pRDataDst = pDataDst + nRI * block_size * width;
			uchar* pRDataBound = pBound + nRI * block_size * width;

			//根据线程ID决定访问的内存块
			int ti = omp_get_thread_num()/* % nThreadNum*/;
			int nBlockWidth = block_size;
			int nBlockHeight = block_size;
			//行列末尾小块处理
			if (nCI == nColIndex - 1 && width % block_size != 0)nBlockWidth = (width) % block_size + block_size;
			if (nRI == nRowIndex - 1 && height % block_size != 0)nBlockHeight = (height) % block_size + block_size;
			nBlockWidth = MIN(nBlockWidth, width);
			nBlockHeight = MIN(nBlockHeight, height);

			//得到每一块的数据
			unsigned char* pCDataSrc = pRDataSrc + nCI * block_size * 3;
			unsigned int* pCDataDst = pRDataDst + nCI * block_size;
			uchar* pCDataBound = pRDataBound + nCI * block_size;
			for (size_t j = 0; j < nBlockHeight; ++j)
			{
				unsigned char* pSubDataSrc = pCDataSrc + j * width * 3;
				unsigned char* pSubBlockDataDst = pDataBlock[ti] + j * nBlockWidth * 3;
				uchar* pSubDataBoundSrc = pCDataBound + j * width;
				uchar* pSubDataBoundDst = pDataBound[ti] + j * nBlockWidth;
				memcpy(pSubBlockDataDst, pSubDataSrc, nBlockWidth * 3);
				memcpy(pSubDataBoundDst, pSubDataBoundSrc, nBlockWidth);
			}

			nCompCount[nBI] = ImgSegmentBlockMark(pDataBlock[ti], nBlockWidth, nBlockHeight, pDataBound[ti], pDataBlockDst[ti], -1, nLastIndex, sigma, k, min_size);
			//将目标结果覆盖到当前区域
			for (size_t j = 0; j < nBlockHeight; ++j)
			{
				unsigned int* pSubDataDst = pCDataDst + j * width;
				unsigned int* pSubBlockDataDst = pDataBlockDst[ti] + j * nBlockWidth;
				memcpy(pSubDataDst, pSubBlockDataDst, nBlockWidth * sizeof(int));
			}
		}

		//根据分块数目,更新索引,需要排除边界
		int nIndexOffset = 0;
		//索引为0的块不用更新索引
		for (int nBI = 1; nBI < nBISum; nBI++)
		{
			int nRI = nBI / nColIndex;
			int nCI = nBI % nColIndex;
			unsigned int* pRDataDst = pDataDst + nRI * block_size * width;
			uchar* pRDataBound = pBound + nRI * block_size * width;
			int nBlockWidth = block_size;
			int nBlockHeight = block_size;
			//行列末尾小块处理
			if (nCI == nColIndex - 1 && width % block_size != 0)nBlockWidth = (width) % block_size + block_size;
			if (nRI == nRowIndex - 1 && height % block_size != 0)nBlockHeight = (height) % block_size + block_size;

			//得到每一块的数据
			unsigned int* pCDataDst = pRDataDst + nCI * block_size;
			uchar* pCDataBound = pRDataBound + nCI * block_size;
			//累加偏移
			nIndexOffset += nCompCount[nBI - 1];
			for (size_t j = 0; j < nBlockHeight; ++j)
			{
				unsigned int* pSubDataDst = pCDataDst + j * width;
				uchar* pSubDataBoundSrc = pCDataBound + j * width;
				for (int i = 0; i < nBlockWidth; ++i)
				{
					if (!pSubDataBoundSrc[i])
					{
						pSubDataDst[i] += nIndexOffset;
					}
				}
			}
		}

		//必定大于0,加入判断只是为了防止意外崩溃
		if (nBISum > 0)
		{
			nLastIndex = nIndexOffset + nCompCount[nBISum - 1];
		}

		for (int i = 0; i < nThreadNum; ++i)
		{
			delete[] pDataBlock[i];
			delete[] pDataBlockDst[i];
			delete[] pDataBound[i];
		}

		delete[] pDataBlock;
		delete[] pDataBlockDst;
		delete[] pDataBound;
		delete[] nCompCount;
	}

	return nLastIndex;
}

int ImgSegBlock(unsigned char* pDataSrc, int width, int height, int nBgValue, unsigned char* pDataDst, int block_size, float sigma, float k, int min_size)
{
	//block_size小于0则不分块,sigma和k有内部处理
	if (pDataSrc == nullptr || width <= 0 || height <= 0 || pDataDst == nullptr || min_size <= 0)
	{
		return -1;
	}

	//首先要找到整幅图的边界,创建对应的mask
	IBufferX* pImage = new IBufferX(pDataSrc, width, height, 3);
	CFindBoundary fb(pImage);
	//如果nBgValue小于零,则不考虑背景
	uchar* pBound = new uchar[width * height]();
	if (nBgValue >= 0)
	{
		fb.Initialize(nBgValue == 0);
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				PtInt ptCur(x, y);
				pBound[y * width + x] = ptCur.IsInBoundInfoLine(fb.m_mBoundInfo[y]);
			}
		}
	}

	delete pImage;

	int num = ImgSegmentBlock(pDataSrc, width, height, pBound, pDataDst, block_size, sigma, k, min_size);

	delete[] pBound;

	return num;
}

//如果区域横款分块,此时要特殊处理
bool IsAreaCrossVBound(AreaLines& area, int& x, int& y)
{
	size_t nLineCount = area.size();
	for (size_t i = 0; i < nLineCount; ++i)
	{
		CLineSeg sg = area[i];

		if (sg.x_s == 0)
		{
			x = sg.x_s;
			y = sg.y;
			return true;
		}
	}

	return false;
}

//如果区域竖跨分块,此时需要特殊处理
bool IsAreaCrossHBound(AreaLines& area, int& x, int& y)
{
	size_t nLineCount = area.size();
	for (size_t i = 0; i < nLineCount; ++i)
	{
		CLineSeg sg = area[i];

		if (sg.y == 0)
		{
			x = sg.x_s;
			y = sg.y;
			return true;
		}
	}

	return false;
}

//注意返回的vetor的数量总比count多1,因为索引是从1开始的,0为边界不处理
size_t TransAreas(unsigned int* buffer, unsigned char* bound, int width, int height, int count, std::vector<AreaLines*>& vAreas)
{
	//分配空间
	int nReal = count + 1;
	vAreas = std::vector<AreaLines*>(nReal);
	for (size_t i = 0; i < nReal; ++i)
	{
		vAreas[i] = new AreaLines();
	}

	for (size_t j = 0; j < height; ++j)
	{
		unsigned int* h_buffer = buffer + width * j;
		unsigned char* h_bound = bound + width * j;

		CLineSeg al(-1, -1, j);
		//是否开启新的线段
		bool bStart = false;
		//bool bEnd = true;
		int cur_idx = -1;
		for (size_t i = 0; i <= width; ++i)
		{
			if (i == width && bStart && cur_idx > 0)
			{
				vAreas[cur_idx]->push_back(al);
				//bStart = false;
				//bEnd = true;
				continue;
			}

			if (h_bound[i])
			{
				if (bStart && cur_idx > 0)
				{
					vAreas[cur_idx]->push_back(al);
					al.x_s = -1;
					cur_idx = -1;
					bStart = false;
				}
			}
			else
			{
				int idx = h_buffer[i];

				if (cur_idx != idx)
				{
					if (cur_idx != -1)
					{
						vAreas[cur_idx]->push_back(al);
					}

					al.x_s = i;
					bStart = true;
					//bEnd = false;
					cur_idx = idx;
				}
				al.x_e = i;
			}
		}
	}

	return 0;
}

int FixBlockBorder(unsigned char* pDataSrc, int width, int height, uchar* pBound, unsigned int* pDataDst, int nLastIndex,
	int block_size, float sigma, float k, int min_size)
{
	if (block_size < 0)
	{
		return -1;
	}

//==========================修复列方向的缝隙========================
	int nColBorders = (width) / block_size - 1;		//计算列方向上缝隙数

	unsigned char* pNewBlock = new unsigned char[block_size * 3 * height * 3]();
	unsigned char* pNewBound = new unsigned char[block_size * 3 * height]();
	unsigned int* pNewDst = new unsigned int[block_size * 3 * height]();

	//对每一个接缝进行处理,因为可能存在很大的连通区域,所以必须整列处理
	for (int nCBIdx = 0; nCBIdx < nColBorders; ++nCBIdx)
	{
		//得到每一块的数据
		unsigned char* pCDataSrc = pDataSrc + nCBIdx * block_size * 3;
		unsigned int* pCDataDst = pDataDst + nCBIdx * block_size;
		unsigned char* pCDataBound = pBound + nCBIdx * block_size;

		//申请块和相邻块的大小
		int nBlockWidth = block_size * 2;
		int nBlockHeight = height;
		//行列末尾小块处理
		if (nCBIdx == nColBorders - 1 && width % block_size != 0)nBlockWidth = (width) % block_size + 2 * block_size;

		//unsigned char* pNewBlock = new unsigned char[nBlockWidth * nBlockHeight * 3]();
		//unsigned char* pNewBound = new unsigned char[nBlockWidth * nBlockHeight]();
		//unsigned int* pNewDst = new unsigned int[nBlockWidth * nBlockHeight]();
		memset(pNewBound, 0, nBlockWidth * nBlockHeight/* * sizeof(char)*/);

		//对于每一个分块的接缝处,需要提取相关的块
		std::map<int, int> miiV;
		int x = block_size - 1;	//竖线的x
		//int y = block_size - 1;	//横线的y

		for (size_t bl = 0; bl < nBlockHeight; ++bl)
		{
			if (!imData(pCDataBound, x, bl, width))
			{
				miiV.insert(std::make_pair(imData(pCDataDst, x, bl, width), bl));
			}

			if (!imData(pCDataBound, x + 1, bl, width))
			{
				miiV.insert(std::make_pair(imData(pCDataDst, x + 1, bl, width), bl));
			}
		}

		//获得原图像
		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			unsigned char* pSubDataSrc = pCDataSrc + h * width * 3;
			unsigned char* pSubBlockDataDst = pNewBlock + h * nBlockWidth * 3;
			memcpy(pSubBlockDataDst, pSubDataSrc, nBlockWidth * 3);
		}

		//获得新的边界
		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			uchar* pSubDataBoundSrc = pCDataBound + h * width;
			unsigned int* pSubDataDst = pCDataDst + h * width;
			uchar* pSubDataBoundDst = pNewBound + h * nBlockWidth;
			for (size_t w = 0; w < nBlockWidth; ++w)
			{
				if (pSubDataBoundSrc[w] || miiV.find(pSubDataDst[w]) == miiV.end())
				{
					pSubDataBoundDst[w] = 1;
				}
			}
		}

		//SaveBufferPPM(pNewBlock, nBlockWidth, nBlockHeight);
		//SaveBoundBufferPPM(pNewBound, nBlockWidth, nBlockHeight);
		int num = ImgSegmentBlockMark(pNewBlock, nBlockWidth, nBlockHeight, pNewBound, pNewDst, -1, 0, sigma, k, min_size);
		//SaveBufferPPM(pNewDst, nBlockWidth, nBlockHeight);

		//转换存储结果,提高更新效率
		std::vector<AreaLines*> vAreas;
		TransAreas(pNewDst, pNewBound, nBlockWidth, nBlockHeight, num, vAreas);

#ifdef CHECK_TRANSRESULT
		for (size_t j = 1; j < vAreas.size(); ++j)
		{
			AreaLines* alsl = vAreas[j];
			for (size_t l = 0; l < alsl->size(); ++l)
			{
				CLineSeg al = (*alsl)[l];
				unsigned int* pSubCData = pNewDst + al.y * nBlockWidth;
				for (size_t u = al.x_s; u <= al.x_e; ++u)
				{
					if (pSubCData[u] != j)
					{
						int a = 0;
					}

					pSubCData[u] = j + 1;
				}
			}
		}

		SaveBufferPPM(pNewDst, nBlockWidth, nBlockHeight);
#endif // 0

#ifndef FIX_VERTICAL
		//将目标结果覆盖到当前区域
		unsigned int* pDstTmp = new unsigned int[nBlockWidth * nBlockHeight];

		//初始化为原来的值
		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			unsigned int* pSubDataDst = pCDataDst + h * width;
			unsigned int* pSubDstTmp = pDstTmp + h * nBlockWidth;
			memcpy(pSubDstTmp, pSubDataDst, nBlockWidth * sizeof(int));
		}
		//std::map<int, int> mib;
		for (size_t bl = 0; bl < nBlockHeight; ++bl)
		{
			if (!imData(pCDataBound, x, bl, width))
			{
				int idxl = imData(pCDataDst, x, bl, width);
				int idxl_new = imData(pNewDst, x, bl, nBlockWidth);
				//将右侧区域中的对应的区域替换成左边的区域
				if (vAreas[idxl_new]->size())
				{
// 					if (mib.find(idxl) == mib.end())
// 					{
						std::vector<CLineSeg>* alsl = vAreas[idxl_new];
						int tmp_idx = idxl;
						int cross_x = -1, cross_y = -1;
						if (IsAreaCrossVBound(*alsl, cross_x, cross_y))
						{
							tmp_idx = imData(pCDataDst, cross_x, cross_y, width);
						}
						for (size_t l = 0; l < alsl->size(); ++l)
						{
							CLineSeg al = (*alsl)[l];
							unsigned int* pSubCDataDst = pDstTmp + al.y * nBlockWidth;
							for (size_t u = al.x_s; u <= al.x_e; ++u)
							{
								pSubCDataDst[u] = tmp_idx;
							}
						}
// 						mib.insert(std::make_pair(idxl, idxl_new));
// 					}
				}

				int idxr = imData(pCDataDst, x + 1, bl, width);
				int idxr_new = imData(pNewDst, x + 1, bl, nBlockWidth);
				//将右侧区域中的对应的区域替换成左边的区域
				if (/*idxr == imData(pDstTmp, x + 1, bl, nBlockWidth) && */vAreas[idxr_new]->size())
				{
// 					if (mib.find(idxr) == mib.end())
// 					{
						std::vector<CLineSeg>* alsl = vAreas[idxr_new];
						int tmp_idx = idxr;
						int cross_x = -1, cross_y = -1;
						if (IsAreaCrossVBound(*alsl, cross_x, cross_y))
						{
							tmp_idx = imData(pCDataDst, cross_x, cross_y, width);
						}
						for (size_t l = 0; l < alsl->size(); ++l)
						{
							CLineSeg al = (*alsl)[l];
							unsigned int* pSubCDataDst = pDstTmp + al.y * nBlockWidth;
							for (size_t u = al.x_s; u <= al.x_e; ++u)
							{
								pSubCDataDst[u] = tmp_idx;
							}
						}
// 						mib.insert(std::make_pair(idxr, idxr_new));
// 					}
				}
			}
		}

		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			unsigned int* pSubDataDst = pCDataDst + h * width;
			unsigned int* pSubDstTmp = pDstTmp + h * nBlockWidth;
			memcpy(pSubDataDst, pSubDstTmp, nBlockWidth * sizeof(int));
		}
#endif // 0

#ifdef BLOCK_RESULT
		for (size_t j = 0; j < nBlockHeight; ++j)
		{
			unsigned int* pSubDataDst = pCDataDst + j * width;
			unsigned int* pSubBlockDataDst = pNewDst + j * nBlockWidth;
			memcpy(pSubDataDst, pSubBlockDataDst, nBlockWidth * sizeof(int));
		}
#endif // 0

		delete[] pDstTmp;
		pDstTmp = nullptr;

		for (std::vector<AreaLines*>::iterator it = vAreas.begin(); it != vAreas.end(); ++it)
		{
			if (*it)
			{
				delete *it;
				*it = nullptr;
			}
		}
	}

	delete[] pNewBlock;
	delete[] pNewBound;
	delete[] pNewDst;

//==========================修复行方向的缝隙========================

	int nRowBorders = (height) / block_size - 1;	//计算行方向上缝隙数

	pNewBlock = new unsigned char[block_size * 3 * width * 3]();
	pNewBound = new unsigned char[block_size * 3 * width]();
	pNewDst = new unsigned int[block_size * 3 * width]();

	//对每一个接缝进行处理,因为可能存在很大的连通区域,所以必须整行处理
	//for (int nRBIdx = 0; nRBIdx < 3; ++nRBIdx)
	for (int nRBIdx = 0; nRBIdx < nRowBorders; ++nRBIdx)
	{
		//得到每一块的数据
		unsigned char* pCDataSrc = pDataSrc + nRBIdx * block_size * width * 3;
		unsigned int* pCDataDst = pDataDst + nRBIdx * block_size * width;
		unsigned char* pCDataBound = pBound + nRBIdx * block_size * width;

		//申请块和相邻块的大小
		int nBlockWidth = width;
		int nBlockHeight = block_size * 2;
		//行列末尾小块处理
		//if (nRBIdx == nColBorders - 1 && width % block_size != 0)nBlockWidth = (width) % block_size + 2 * block_size;
		if (nRBIdx == nRowBorders - 1 && height % block_size != 0)nBlockHeight = (height) % block_size + 2 * block_size;

		//unsigned char* pNewBlock = new unsigned char[nBlockWidth * nBlockHeight * 3]();
		//unsigned char* pNewBound = new unsigned char[nBlockWidth * nBlockHeight]();
		//unsigned int* pNewDst = new unsigned int[nBlockWidth * nBlockHeight]();
		memset(pNewBound, 0, nBlockWidth * nBlockHeight/* * sizeof(char)*/);

		//对于每一个分块的接缝处,需要提取相关的块
		std::map<int, int> miiH;
		int y = block_size - 1;	//横线的y

		for (size_t bl = 0; bl < width; ++bl)
		{
			if (!imData(pCDataBound, bl, y, width))
			{
				miiH.insert(std::make_pair(imData(pCDataDst, bl, y, width), bl));
			}

			if (!imData(pCDataBound, bl, y + 1, width))
			{
				miiH.insert(std::make_pair(imData(pCDataDst, bl, y + 1, width), bl));
			}
		}

		//获得原图像
		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			unsigned char* pSubDataSrc = pCDataSrc + h * width * 3;
			unsigned char* pSubBlockDataDst = pNewBlock + h * nBlockWidth * 3;
			memcpy(pSubBlockDataDst, pSubDataSrc, nBlockWidth * 3);
		}

		//获得新的边界
		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			uchar* pSubDataBoundSrc = pCDataBound + h * width;
			unsigned int* pSubDataDst = pCDataDst + h * width;
			uchar* pSubDataBoundDst = pNewBound + h * nBlockWidth;
			for (size_t w = 0; w < nBlockWidth; ++w)
			{
				if (pSubDataBoundSrc[w] || miiH.find(pSubDataDst[w]) == miiH.end())
				{
					pSubDataBoundDst[w] = 1;
				}
			}
		}

		//SaveBufferPPM(pNewBlock, nBlockWidth, nBlockHeight);
		//SaveBoundBufferPPM(pNewBound, nBlockWidth, nBlockHeight);
		int num = ImgSegmentBlockMark(pNewBlock, nBlockWidth, nBlockHeight, pNewBound, pNewDst, -1, 0, sigma, k, min_size);
		//SaveBufferPPM(pNewDst, nBlockWidth, nBlockHeight);

		//转换存储结果,提高更新效率
		std::vector<AreaLines*> vAreas;
		TransAreas(pNewDst, pNewBound, nBlockWidth, nBlockHeight, num, vAreas);

#ifdef CHECK_TRANSRESULT
		for (size_t j = 1; j < vAreas.size(); ++j)
		{
			std::vector<CLineSeg>* alsl = vAreas[j];
			for (size_t l = 0; l < alsl->size(); ++l)
			{
				CLineSeg al = (*alsl)[l];
				unsigned int* pSubCData = pNewDst + al.y * nBlockWidth;
				for (size_t u = al.x_s; u <= al.x_e; ++u)
				{
					pSubCData[u] = j + 1;
				}
			}
		}
		//SaveBufferPPM(pNewDst, nBlockWidth, nBlockHeight);
#endif // 0

#ifndef FIX_HORIZONTAL
		unsigned int* pDstTmp = new unsigned int[nBlockWidth * nBlockHeight];

		//初始化为原来的值
		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			unsigned int* pSubDataDst = pCDataDst + h * width;
			unsigned int* pSubDstTmp = pDstTmp + h * nBlockWidth;
			memcpy(pSubDstTmp, pSubDataDst, nBlockWidth * sizeof(int));
		}

		//_tstring stSaveOld = _T("D:\\pOldDst") + CStdTpl::ConvertToString(nRBIdx) +_T(".ppm");
		//std::string sSaveOld = CStdStr::ws2s(stSaveOld);
		//SaveBufferPPM(pDstTmp, nBlockWidth, nBlockHeight, sSaveOld.c_str());

		//_tstring stSavePath = _T("D:\\pNewDst") + CStdTpl::ConvertToString(nRBIdx) +_T(".ppm");
		//std::string sSavePath = CStdStr::ws2s(stSavePath);
		//SaveBufferPPM(pNewDst, nBlockWidth, nBlockHeight, sSavePath.c_str());

		//_tstring stAllPath = _T("D:\\all") + CStdTpl::ConvertToString(2 * nRBIdx) +_T(".ppm");
		//std::string sAllPath = CStdStr::ws2s(stAllPath);
		//SaveBufferPPM(pDataDst, width, height, sAllPath.c_str());

		//std::map<int, int> mib;
		for (size_t bl = 0; bl < nBlockWidth; ++bl)
		{
			if (!imData(pCDataBound, bl, y, width))
			{
				//读取原来的值
				int idxd = imData(pCDataDst, bl, y, width);
				int idxd_new = imData(pNewDst, bl, y, nBlockWidth);
				int idxu = imData(pCDataDst, bl, y + 1, width);
				int idxu_new = imData(pNewDst, bl, y + 1, nBlockWidth);

				//区域替换
				if (/*idxd == imData(pDstTmp, bl, y, width) && */vAreas[idxd_new]->size())
				{
					// 					if (mib.find(idxd) == mib.end())
					// 					{
					std::vector<CLineSeg>* alsl = vAreas[idxd_new];
					int tmp_idx = idxd;
					int cross_x = -1, cross_y = -1;
					if (IsAreaCrossHBound(*alsl, cross_x, cross_y))
					{
						tmp_idx = imData(pCDataDst, cross_x, cross_y, width);
					}
					for (size_t l = 0; l < alsl->size(); ++l)
					{
						CLineSeg al = (*alsl)[l];
						unsigned int* pSubCDataDst = pDstTmp + al.y * nBlockWidth;
						for (size_t u = al.x_s; u <= al.x_e; ++u)
						{
							pSubCDataDst[u] = tmp_idx;
						}
					}
					// 						mib.insert(std::make_pair(idxd, idxd_new));
					// 					}
				}

 				if (/*idxu == imData(pDstTmp, bl, y + 1, nBlockWidth) && */vAreas[idxd_new]->size())
 				{
 					// 					if (mib.find(idxu) == mib.end())
 					// 					{
					std::vector<CLineSeg>* alsl = vAreas[idxu_new];
					int tmp_idx = idxu;
					int cross_x = -1, cross_y = -1;
					if (IsAreaCrossHBound(*alsl, cross_x, cross_y))
					{
						tmp_idx = imData(pCDataDst, cross_x, cross_y, width);
					}
					for (size_t l = 0; l < alsl->size(); ++l)
					{
						CLineSeg al = (*alsl)[l];
						unsigned int* pSubCDataDst = pDstTmp + al.y * nBlockWidth;
						for (size_t u = al.x_s; u <= al.x_e; ++u)
						{
							pSubCDataDst[u] = tmp_idx;
						}
					}
 					// 						mib.insert(std::make_pair(idxu, idxu_new));
 					// 					}
 				}
			}
		}

		for (size_t h = 0; h < nBlockHeight; ++h)
		{
			unsigned int* pSubDataDst = pCDataDst + h * width;
			unsigned int* pSubDstTmp = pDstTmp + h * nBlockWidth;
			memcpy(pSubDataDst, pSubDstTmp, nBlockWidth * sizeof(int));
		}

		//_tstring stAllPathAfter = _T("D:\\all") + CStdTpl::ConvertToString(2 * nRBIdx + 1) +_T(".ppm");
		//std::string sAllPathAfter = CStdStr::ws2s(stAllPathAfter);
		//SaveBufferPPM(pDataDst, width, height, sAllPathAfter.c_str());

#endif // 0

#ifdef BLOCK_RESULT
		for (size_t j = 0; j < nBlockHeight; ++j)
		{
			unsigned int* pSubDataDst = pCDataDst + j * width;
			unsigned int* pSubBlockDataDst = pNewDst + j * nBlockWidth;
			memcpy(pSubDataDst, pSubBlockDataDst, nBlockWidth * sizeof(int));
		}
#endif // 0

		delete[] pDstTmp;
		pDstTmp = nullptr;

		for (std::vector<AreaLines*>::iterator it = vAreas.begin(); it != vAreas.end(); ++it)
		{
			if (*it)
			{
				delete *it;
				*it = nullptr;
			}
		}
	}

	delete[] pNewBlock;
	delete[] pNewBound;
	delete[] pNewDst;

	return 0;
}

int ImgSegBlockMark(unsigned char* pDataSrc, int width, int height, int nBgValue, unsigned int* pDataDst, int block_size, float sigma, float k, int min_size)
{
	//block_size小于0则不分块,sigma和k有内部处理
	if (pDataSrc == nullptr || width <= 0 || height <= 0 || pDataDst == nullptr || min_size <= 0)
	{
		return -1;
	}

	//首先要找到整幅图的边界,创建对应的mask
	IBufferX* pImage = new IBufferX(pDataSrc, width, height, 3);
	CFindBoundary fb(pImage);

	//如果nBgValue小于零,则不考虑背景
	uchar* pBound = new uchar[width * height]();
	if (nBgValue >= 0)
	{
		fb.Initialize(nBgValue == 0);
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				PtInt ptCur(x, y);
				pBound[y * width + x] = ptCur.IsInBoundInfoLine(fb.m_mBoundInfo[y]);
			}
		}
	}
	delete pImage;
	//必须初始化为0
	int nLastIndex = 0;

	nLastIndex = ImgSegmentBlockMark(pDataSrc, width, height, pBound, pDataDst, block_size, nLastIndex, sigma, k, min_size);

	if (block_size > 0 && (width > block_size || height > block_size))
	{
		FixBlockBorder(pDataSrc, width, height, pBound, pDataDst, nLastIndex, block_size, sigma, k, min_size);
	}

	delete[] pBound;

	return nLastIndex;
}

int KMeansSegment(unsigned char* pDataSrc, int width, int height, int nBgValue, unsigned int* pDataDst, int block_size, float sigma, float k, int min_size)
{
	//block_size小于0则不分块,sigma和k有内部处理
	if (pDataSrc == nullptr || width <= 0 || height <= 0 || pDataDst == nullptr || min_size <= 0)
	{
		return -1;
	}

	//首先要找到整幅图的边界,创建对应的mask
	IBufferX* pImage = new IBufferX(pDataSrc, width, height, 3);
	CFindBoundary fb(pImage);

	//如果nBgValue小于零,则不考虑背景
	uchar* pBound = new uchar[width * height]();
	if (nBgValue >= 0)
	{
		fb.Initialize(nBgValue == 0);
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				PtInt ptCur(x, y);
				pBound[y * width + x] = ptCur.IsInBoundInfoLine(fb.m_mBoundInfo[y]);
			}
		}
	}
	delete pImage;
	//必须初始化为0
	int nLastIndex = 0;

	nLastIndex = ImgSegmentBlockMark(pDataSrc, width, height, pBound, pDataDst, block_size, nLastIndex, sigma, k, min_size);

	//if (block_size > 0 && (width > block_size || height > block_size))
	//{
	//	FixBlockBorder(pDataSrc, width, height, pBound, pDataDst, nLastIndex, block_size, sigma, k, min_size);
	//}

	if (nLastIndex)
	{
		//根据kmeans对所得到的过于精细的分割进行聚类处理,参考因子为每个小块中rgb的均值
		//转换存储结果,提高更新效率
		std::vector<AreaLines*> vAreas;
		TransAreas(pDataDst, pBound, width, height, nLastIndex, vAreas);
#ifdef CHECK_TRANSRESULT
		for (size_t j = 1; j < vAreas.size(); ++j)
		{
			std::vector<CLineSeg>& alsl = vAreas[j]->vLines;
			for (size_t l = 0; l < alsl.size(); ++l)
			{
				CLineSeg al = alsl[l];
				unsigned int* pSubCData = pDataDst + al.y * width;
				for (size_t u = al.x_s; u <= al.x_e; ++u)
				{
					pSubCData[u] = j + 1;
				}
			}
		}
#endif // 0

		int& nWidth = width;
		int& nHeight = height;
		int nBandNum = 3;

		//对得到的每一个结果取得rgb的均值,作为聚类的参考
		int size = nLastIndex;				//样本个数,实际上可能比nLastIndex小
		const int dim = nBandNum;			//Dimension of feature
		const int cluster_num = 5;			//Cluster number

		const size_t lMemSize = size * dim;
		double* data = new double[lMemSize * sizeof(double)]();
		int nKCount = 0;
		for (int i = 1; i <= size; ++i)
		{
			//分别计算每个区域的rgb的均值,并且注意是rgb是连续在一起的
			std::vector<CLineSeg>* alsl = vAreas[i];
			long lrgbSum[3] = {0};
			long lPixCount = 0;
			for (size_t l = 0; l < alsl->size(); ++l)
			{
				CLineSeg al = (*alsl)[l];
				unsigned char* pSubData = pDataSrc + al.y * width * 3;
				for (size_t u = al.x_s; u <= al.x_e; ++u)
				{
					//需要找到原图上对应的点,然后分别计算出rgb的和
					for (size_t c = 0; c < 3; ++c)
					{
						lrgbSum[c] += pSubData[u * 3 + c];
					}
					++lPixCount;
				}
			}

			if (lPixCount)
			{
				for (size_t c = 0; c < 3; ++c)
				{
					//i从1开始,整数相除
					data[nKCount * 3 + c] = lrgbSum[c] / lPixCount;
				}
				++nKCount;
			}
		}

		//实际数目
		size = MIN(size, nKCount);

		KMeans* kmeans = new KMeans(dim, cluster_num);
		int* labels = new int[size];
		kmeans->SetInitMode(KMeans::InitUniform);
		kmeans->Cluster(data, size, labels);

 		for (int i = 0; i < size; ++i)
 		{
 			//printf("%f, %f, %f belongs to %d cluster\n", data[i*dim + 0], data[i*dim + 1], data[i*dim + 2], labels[i]);
 			std::vector<CLineSeg>* alsl = vAreas[i + 1];
 			for (size_t l = 0; l < alsl->size(); ++l)
 			{
 				CLineSeg al = (*alsl)[l];
 				unsigned int* pSubCData = pDataDst + al.y * nWidth;
 				for (size_t u = al.x_s; u <= al.x_e; ++u)
 				{
 					pSubCData[u] = labels[i] + 1;	//labels是从0开始的
 				}
 			}
 		}

		delete[] data;
		delete[] labels;
		delete kmeans;
		//CStdTpl::DelPointerSafely(data, true);
		//CStdTpl::DelPointerSafely(labels, true);
		//CStdTpl::DelPointerSafely(kmeans);

		for (std::vector<AreaLines*>::iterator it = vAreas.begin(); it != vAreas.end(); ++it)
		{
			if (*it)
			{
				delete *it;
				*it = nullptr;
			}
		}

		nLastIndex = cluster_num;
	}

	delete[] pBound;

	return nLastIndex;
}

更多的交流,欢迎留言。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值