📄 captureproc.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 ¶m)
{
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 + -