⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 captureproc.cpp

📁 通过使机器人进行简单的图像识别
💻 CPP
字号:
/********************************************************************
*
* =-----------------------------------------------------------------=
* =                          ____          _________                =
* =                         / _  \         \___  __/                =
* =                        / /_/ /     ____   / /                   =
* =                       /  _   \ ● / _  \ / /                    =
* =                      /  /_/  // // / / // /                     =
* =                      \______//_//_/ /_//_/                      =
* =                                                                 =
* =             Copyright (c) BIN Technology studio,2004            =
* =                           LET'Z BT                              =
* =-----------------------------------------------------------------=
*
*	FileName	: CaptureProc.cpp
*	Description	: 图像捕捉+颜色切分
*
*	Author		: 风间苍月(TuQbasic)
*	Email		: tuqbasic@sohu.com
*
*	Create		: 2004.04.12
*	LastChange	: 2004.05.07
*
*	History		: 
********************************************************************/ 
#include "stdafx.h"
#include <string>
using namespace std;

#include "Engine\hSystem.h"
#include "Engine\hGraphic2D.h"
#include "Engine\hPackage.h"
#include "Engine\hInput.h"
#include "Engine\hMedia.h"

#include "SMode.h"
#include "HSVRGB.h"
#include "XImage.h"
#include "Target.h"
#include "CaptureBuf.h"
#include "RoboCar.h"

const float MID_POSITION= .5;
const int	RET_TRUE= 1;
const int	RET_FALSE= 0;
const int	RET_EXIT= 3;

typedef struct _sTargetRatio
{
	float StandLeft;
	float StandRight;

	float RunLeft;
	float RunRight;

	_sTargetRatio(void)
	{
		StandLeft= .4;
		StandRight= .6;

		RunLeft= .25;
		RunRight= .75;
	}
}TARGETRATIO;

typedef struct _sProtoParam
{
	unsigned int open_time;
	unsigned int close_time;

	string strBMP;
	
	SHSVRGB::INCISECOLOR	ic;
	TARGETRATIO				target;
	bool					startupleft;	// 启动时居左手

	_sProtoParam(void)
	{
		open_time= 0;
		close_time= 0;

		strBMP= "Static.bmp";

		ic.low.H= 0;
		ic.low.S= .5;
		ic.low.V= .5;

		ic.top.H= 90;
		ic.top.S= 1.;
		ic.top.V= 1.;

		startupleft= true;
	}
}PROTOPARAM;

#define WIN_WIDTH	640
#define WIN_HEIGHT	480

// 外部变量,窗口相关
extern HINSTANCE	g_hInst;
extern HWND			g_hWnd;
extern bool			g_bActive;
extern int			g_sViewMode;
extern int			g_sUpdateMode;
extern HACCEL		g_hAccelTable;

// 图形接口
LPI_DISPLAY2D		g_pDisplay= NULL;

LPI_DXSURFACE		g_psStatic= NULL;		// 静态测试图像

LPI_DXSURFACE		g_psBuf= NULL;			// 调整图像大小所用的缓冲区

LPI_AVIPLAYER		g_pAvi= NULL;			// 视频播放接口

// 鼠标输入接口
LPI_MOUSE			g_pMouse= NULL;
LPI_KEYBOARD		g_pKeyboard= NULL;

PROTOPARAM			g_proparam;
CAPTUREPARAM		g_capparam;

CXImage				g_ximage;		// 图像切割
CTarget				g_target;		// 目标搜索
CCaptureBuf			g_capbuf;		// 图像采集缓冲

CRoboCar			g_car;			// 机器人

// 有退出消息返回false
bool MessageProc(void)
{
	static bool bExit= false;
	if (bExit) return false;

	MSG msg;
	if( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE ) )
	{
		if( GetMessage( &msg, NULL, 0, 0 ) )
		{
			if (!TranslateAccelerator(msg.hwnd, g_hAccelTable, &msg)) 
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else
		{
			bExit= true;
			return false;
		}
	}
	return true;
}

// 读取参数
void LoadParam(const char *szFile, PROTOPARAM &param)
{
	LPI_FILEINI pIni= CreateFileINI(szFile);
	if (pIni)
	{
		param.strBMP= pIni->getKeyValue("Filename", "File");

		param.open_time= pIni->getKeyValueLong("Open", "Incise");
		param.close_time= pIni->getKeyValueLong("Close", "Incise");

		param.ic.low.H= pIni->getKeyValueDouble("HLow", "Incise");
		param.ic.low.S= pIni->getKeyValueDouble("SLow", "Incise");
		param.ic.low.V= pIni->getKeyValueDouble("VLow", "Incise");

		param.ic.top.H= pIni->getKeyValueDouble("HTop", "Incise");
		param.ic.top.S= pIni->getKeyValueDouble("STop", "Incise");
		param.ic.top.V= pIni->getKeyValueDouble("VTop", "Incise");

		g_target.LoadRatioParam(pIni);

		param.target.RunLeft= pIni->getKeyValueDouble("RunLeft", "RunCar");
		param.target.RunRight= pIni->getKeyValueDouble("RunRight", "RunCar");
		param.target.StandLeft= pIni->getKeyValueDouble("StandLeft", "RunCar");
		param.target.StandRight= pIni->getKeyValueDouble("StandRight", "RunCar");

		param.startupleft= pIni->getKeyValueBool("StartUpLeft", "RunCar");
	}
	SAFE_DELETE(pIni);
}

bool InitInterface(void)
{
	// 初始化DirectX
	if (FAILED(CreateDisplay2D(g_hWnd, WIN_WIDTH, WIN_HEIGHT, &g_pDisplay)))
		return false;

	g_pMouse= CreateMouse(g_hInst, g_hWnd);
	g_pKeyboard= CreateKeyboard(g_hInst, g_hWnd);

	g_psBuf= g_pDisplay->CreateSurface(g_capparam.cap_width, g_capparam.cap_height);

	// 加载静态测试图
	g_psStatic= g_pDisplay->CreateSurfaceFromIMG(g_proparam.strBMP.c_str());

	// 初始化采集卡和相关资源
	g_capparam.cap_hwnd= g_hWnd;
	if (FAILED(InitCaptureCard(g_capparam)))
		return false;

	// 尝试初始化缓冲区直到成功
	while(!g_capbuf.Alloc(g_capparam.cap_card))
		;

	g_target.Alloc(g_capparam.cap_width, g_capparam.cap_height);
	g_ximage.InitIncise(g_capparam.cap_width, g_capparam.cap_height, g_proparam.ic, true);

	MessageBeep(MB_OK);
	return true;
}

void ReleaseInterface(void)
{
	// 释放采集卡和采集卡相关资源
	g_capbuf.Free();
	g_target.Free();
	g_ximage.EndIncise();
	ReleaseCard(g_capparam);

	// 释放DirectX
	SAFE_RELEASE(g_pAvi);
	SAFE_RELEASE(g_pMouse);
	SAFE_RELEASE(g_pKeyboard);
	SAFE_RELEASE(g_psBuf);
	SAFE_RELEASE(g_psStatic);
	SAFE_RELEASE(g_pDisplay);
}

// 根据当前选项获取更新的图像数据
void UpdateBitmap(void)
{
	switch(g_sUpdateMode)
	{
	case SMODE_UPDATE_STATIC:
		g_capbuf.UpdateBuffer(g_psStatic);
		break;
	case SMODE_UPDATE_VIDEO:
		if (!g_pAvi->UpdateFrame()) g_pAvi->Play();
		g_capbuf.UpdateBuffer(g_psBuf);
		break;
	case SMODE_UPDATE_CAPTURE:
		g_capbuf.UpdateBuffer(g_capparam.cap_card);
		break;
	default:
		break;
	}
}

// 输出颜色分量
void ShowColor(void)
{
	// 获取鼠标位置并获取ARGB颜色
	if (!g_pMouse) return;

	g_pMouse->RecieveMouseInput();

	POINT pt;
	pt.x= g_pMouse->GetMouseX();
	pt.y= g_pMouse->GetMouseY();

	g_pDisplay->Desktop2Client(pt);

	g_pDisplay->GetBackSurface()->GetGraphic()->BeginPaint();
	unsigned int dw= g_pDisplay->GetBackSurface()->GetGraphic()->GetPixel(pt.x, pt.y);
	g_pDisplay->GetBackSurface()->GetGraphic()->EndPaint();

	// 转换为HSV并输出
	HDC hDC;
	g_pDisplay->GetBackSurface()->GetDC(&hDC);
	{
		::SetBkColor(hDC, 0x00000000);
		::SetTextColor(hDC, 0x0000FF00);
		char c[1024];

		SHSVRGB::HSV hsv;
		SHSVRGB::RGB2HSVEx(dw, hsv);

		sprintf(c, "H= %f, S= %f, V= %f", hsv.H, hsv.S, hsv.V);
		TextOut(hDC, 0, 20, c, strlen(c));

		unsigned char R, G, B;
		R= (dw & 0x00FF0000)>>16;
		G= (dw & 0x0000FF00)>>8;
		B= (dw & 0x000000FF);

		sprintf(c, "R= %d, G= %d, B= %d", R, G, B);
		TextOut(hDC, 0, 40, c, strlen(c));
	}
	g_pDisplay->GetBackSurface()->ReleaseDC(hDC);
}

void ShowUpdate(void)
{
	switch(g_sUpdateMode)
	{
	case SMODE_UPDATE_STATIC:
		g_pDisplay->FillSurface(g_psStatic, g_pDisplay->GetBackSurface(), NULL, NULL);
		break;
	case SMODE_UPDATE_VIDEO:
		g_pDisplay->FillSurface(g_psBuf, g_pDisplay->GetBackSurface(), NULL, NULL);
		break;
	case SMODE_UPDATE_CAPTURE:
		g_capbuf.Bitmap2Surface(g_psBuf);
		g_pDisplay->FillSurface(g_psBuf, g_pDisplay->GetBackSurface());
		break;
	default:
		break;
	}
}

void ShowIncise(void)
{
	g_ximage.ConvertOutDSASM(g_psBuf);
	g_pDisplay->FillSurface(g_psBuf, g_pDisplay->GetBackSurface());
}

void ShowTarget(void)
{	
	g_target.DrawTargetBorderASM(g_ximage.GetImageBuffer());
	g_target.DrawTargetRect(g_ximage.GetImageBuffer());
	g_ximage.ConvertOutDSASM(g_psBuf);
	g_pDisplay->FillSurface(g_psBuf, g_pDisplay->GetBackSurface());
}

void ChangeUpdateMode(unsigned long PreMode, unsigned long NewMode)
{
	if (NewMode == SMODE_UPDATE_VIDEO)
	{	if (!g_pAvi)
		{
			g_pAvi= CreateAVIPlayer();
			g_pAvi->RenderFile("Sample.AVI", g_pDisplay->GetDirectDraw7());
			g_pAvi->SetRenderTarget(g_psBuf->GetDirectSurface7());
			g_pAvi->Play();
		}
		g_pAvi->Continue();
	}
	else if (PreMode == SMODE_UPDATE_VIDEO)
	{
		g_pAvi->Pause();
	}
}

// 手动
void ManualCar(void)
{
	if (!g_pKeyboard) return;

	g_pKeyboard->RecieveKeyboardInput();
	if (g_pKeyboard->IsUp2Down(DIK_E))
	{
		g_car.SetStatus(ACTION_FORWARD);
	}
	else if (g_pKeyboard->IsUp2Down(DIK_D))
	{
		g_car.SetStatus(ACTION_BACKWARD);
	}
	else if (g_pKeyboard->IsUp2Down(DIK_S))
	{
		g_car.SetStatus(ACTION_LEFT);
	}
	else if (g_pKeyboard->IsUp2Down(DIK_F))
	{
		g_car.SetStatus(ACTION_RIGHT);
	}
	else if (g_pKeyboard->IsUp2Down(DIK_R))
	{
		g_car.SetStatus(ACTION_IDLE);
	}
}

// 图像处理,更新图像并搜索目标,搜索到目标返回true
bool ImageProcess(void)
{
	// 更新采集图像和颜色切分
	UpdateBitmap();

	// 为避免冗余,画面更新操作插在图像处理中完成
	if (g_sViewMode == SMODE_VIEW_UPDATE)
		ShowUpdate();

	g_ximage.InciseImageASM(g_capbuf.GetBitmapInfo());
	if (g_sViewMode == SMODE_VIEW_INCISE)
		ShowIncise();

	// 开闭运算
	for (int i= 0; i < g_proparam.close_time; i++)
		g_ximage.OperatorClose();
	for (int j= 0; j < g_proparam.open_time; j++)
		g_ximage.OperatorOpen();

	if (g_sViewMode == SMODE_VIEW_FLITER)
		ShowIncise();

	// 查找目标
	bool b= g_target.UpdateTarget(g_ximage.GetImageBuffer());

	if (g_sViewMode == SMODE_VIEW_TARGET)
		ShowTarget();

	ShowColor();
	g_pDisplay->Present();

	return b;
}

// 出牛郎区后向前直冲
void Rush(bool bLeftHand/*是否于左手出发*/)
{
	// 冲出一段
	g_car.ActionSlave(ACTION_BACKWARD, 1000);

	// 朝外偏转一定方向
	if (bLeftHand)
		g_car.ActionSlave(ACTION_LEFT, 1000);
	else
		g_car.ActionSlave(ACTION_RIGHT, 1000);

	// 裸走一段
	g_car.ActionSlave(ACTION_BACKWARD, 3000);

	// 向内折一段
	if (bLeftHand)
		g_car.ActionSlave(ACTION_RIGHT, 1000);
	else
		g_car.ActionSlave(ACTION_LEFT, 1000);

	// 继续裸走
	g_car.ActionSlave(ACTION_BACKWARD, 4000);

	// 再内折一段
	if (bLeftHand)
		g_car.ActionSlave(ACTION_RIGHT, 1500);
	else
		g_car.ActionSlave(ACTION_LEFT, 1000);

	g_car.ActionSlave(ACTION_BACKWARD, 4000);

	// 转一下
	if (bLeftHand)
		g_car.ActionSlave(ACTION_LEFT, 2000);
	else
		g_car.ActionSlave(ACTION_RIGHT, 2000);
}

// 已发现目标在lTime时间范围内校准目标位置,否则返回false
// 当目标在左手边,需左校准;在右手边,需右校准
// 但有可能当作完左校准目标又出现在需右校准的区域,反之亦然。
// 这样有可能导致陷入反复左右校准的死循环
// 设置一个校准动作改变次数,当反复左右校准一定次数后跳出
int LocateTarget(long lTime, bool bLeftHand= false/*上一次在左手看见目标*/)
{
	const int ActionMaxSwapTime= 5;
	int ActionLast= bLeftHand ? ACTION_LEFT : ACTION_RIGHT;		// 上一个校准动作使用的
	int ActionSwapTime= 0;										// 校准动作改变的次数
		
	g_car.SetStatus(ActionLast);

	RECT r;
	while(MessageProc())
	{
		if (tOnFPS())
		{
			bool b= ImageProcess();
			if (b)
			{
				g_target.GetTarget(r);
				float mid= (float)((r.left + r.right) / 2) / (float)g_capparam.cap_width;
				if ((mid > g_proparam.target.StandLeft) && (mid < g_proparam.target.StandRight))
				{
					return RET_TRUE;
				}
				else
				{	// 记录左右手
					bLeftHand= mid < MID_POSITION ? true : false;

					// 做动作调整
					int Action= bLeftHand ? ACTION_LEFT : ACTION_RIGHT;
					if (Action != ActionLast)
					{
						ActionLast= Action;
						ActionSwapTime++;

						// 校准动作改变次数过多,返回
						if (ActionSwapTime > ActionMaxSwapTime)
						{
							return RET_TRUE;
						}
						// 执行校准动作						
						g_car.SetStatus(Action);
					}
				}
			}
			else if (g_target.IsLost())
			{
				return false;
			}
			else
			{
				g_car.SetStatus(ActionLast);
			}
			// ***********************************************
			ManualCar();
		}
		g_car.ResponseTick();
		if (g_car.IsShutDown())
			return RET_EXIT;
	}
	return RET_TRUE;
}

// 未发现目标,在lTime时间范围内找到并校准目标为之,否则返回false
int SearchTarget(long lTime, bool bLeftHand= false/*上一次在左手看见目标*/)
{
	unsigned long Action= bLeftHand ? ACTION_LEFT : ACTION_RIGHT;
	g_car.SetStatus(Action);
	
	while(MessageProc())
	{
		if (tOnFPS())
		{
			bool b= ImageProcess();
			if (b)
			{	// 目标在视野中出现

				RECT r;
				g_target.GetTarget(r);
				float mid= (float)((r.left + r.right) / 2) / (float)g_capparam.cap_width;
				bLeftHand= (mid < MID_POSITION) ? true : false;

				if (LocateTarget(lTime, bLeftHand))
					return RET_TRUE;
				else
				{
					g_car.SetStatus(Action);
				}
			}
			else
			{	// 没办法,继续转
				g_car.SetStatus(Action);
			}

			// ***********************************************
			ManualCar();
		}
		g_car.ResponseTick();
		if (g_car.IsShutDown())
			return RET_EXIT;
	}
	return RET_TRUE;
}

// 主循环
void MainProcess(void)
{
	LoadParam(".\\RoboSight.ini", g_proparam);
	if (!InitInterface())
		Failed("初始化操作失败!");

	while(1)
	{
		g_car.WaitMessage();		// 等待启动消息

		RECT r;
		bool bLeftHand= g_proparam.startupleft;
		Rush(bLeftHand);

		SearchTarget(0, bLeftHand);
		g_car.SetStatus(ACTION_FORWARD);

		while(MessageProc())
		{	
			if (tOnFPS())
			{
				bool b= ImageProcess();
				// 处理查找的目标,操纵机器***********************
				if (b)
				{
					g_target.GetTarget(r);
					float mid= (float)((r.left + r.right) / 2) / (float)g_capparam.cap_width;
					if ((mid > g_proparam.target.RunLeft) && (mid < g_proparam.target.RunRight))
					{	// 仍然可以向前开,但是要记录左右手
						if (mid < MID_POSITION)
							bLeftHand= true;		// 目标在左手
						else
							bLeftHand= false;		// 目标在右手
					}
					else
					{	// 需校正方向
						if (RET_EXIT == LocateTarget(0, bLeftHand))
							break;
						g_car.SetStatus(ACTION_FORWARD);
					}
				}
				else if (g_target.IsLost())
				{	// 丢失目标,需查找
					if (RET_EXIT == SearchTarget(0, bLeftHand))
						break;
					g_car.SetStatus(ACTION_FORWARD);
				}
				else
				{	// 暂时丢失,保持状态

				}
				// ***********************************************
				ManualCar();
			}
			g_car.ResponseTick();
			if (g_car.IsShutDown())
				break;
		}
		g_car.SetStatus(ACTION_IDLE);
	}
	ReleaseInterface();
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -