<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.5">
  <POU Name="Prg_Vision" Id="{819b3739-c132-4973-9cae-fce9a2de95d7}" SpecialFunc="None">
    <Declaration><![CDATA[PROGRAM Prg_Vision
VAR
	aColorRed 			: TcVnVector4_LREAL := [255, 0, 0];
	aColorGreen 		: TcVnVector4_LREAL := [0, 255, 0];
	aColorBlue 			: TcVnVector4_LREAL := [0, 0, 255];
	
	FBProfiler			: Profiler;
		
	hr  				: HRESULT;
    fbCamera			: FB_VN_SimpleCameraControl;
    eState				: ETcVnCameraState;
     
	udWidth				: UDINT;
	udHeight			: UDINT;	
    ipImageIn 			: ITcVnImage;
    ipImageInDisp 		: ITcVnDisplayableImage;
	
	
	ipImageRes 			: ITcVnImage;
	ipImageResDisp 		: ITcVnDisplayableImage;

	stDrawRoi			: TcVnRectangle_UDINT := (nX := 900, nY := 850, nWidth := 1000, nHeight := 1000);	
	stRoi				: TcVnRectangle_UDINT := (nX := 900, nY := 850, nWidth := 1000, nHeight := 1000);
	ipContourList1		: ITcVnContainer;
	
	ipImageRegion 		: ITcVnImage;
    ipImageRegionDisp	: ITcVnDisplayableImage;
	
	aOffset1			: TcVnPoint;
	nNumberOfElements	: ULINT;
	iRef1				: ULINT;
	fArea1				: LREAL;
	ipContour1			: ITcVnContainer;
	iDetectNum1			: INT;
	fCircularity		:LREAL;
	fAngle_M1			: LREAL;	
	sAngle_M1			: STRING(20);	
	stRectangle			: TcVnRotatedRectangle;		
	
	ipStructimage 		: ITcVnImage;
	certerofmass		: TcVnPoint2_LREAL;
	aEndPoint			: TcVnPoint2_REAL;
	
	eDirection			: ETcVnEdgeDirection := TCVN_ED_DARK_TO_LIGHT;	
	fMinStrength		: REAL := 50;
	nSearchLines		: UDINT := 101;
	fSearchLineDist		: REAL := 3;
	nMaxThickness		: UDINT := 1;
	nSubpixIter			: UDINT := 10;
	eAlgorithm			: ETcVnEdgeDetectionAlgorithm := TCVN_EDA_INTERPOLATION;
	fAvgStrength		: REAL;
	ipEdgePoints		: ITcVnContainer;	
	aLine				: TcVnVector4_LREAL;
	aStartPoint			: TcVnPoint2_REAL;
	fAngle_M2			: LREAL;
	sAngle_M2			: STRING(20);
	
	fHideth				: REAL;
	circleofMass		: TcVnPoint2_LREAL;
	L					: LREAL;
	X1					: LREAL;
	Y1					: LREAL;
	L1					: REAL;
	
	lasttime			: LREAL;
	maxtime				: LREAL;
	averagetime			: REAL;
	mintime				: REAL;
	bFindAngle_M1Temp   : BOOL;
	
	// 形态学开闭算法切换
	eOperator			: ETcVnMorphologicalOperator := ETcVnMorphologicalOperator.TCVN_MO_CLOSING;
	// 形态学卷积核
	udStructElement		: UDINT := 10;
	// 形态学启用		
	bmorphology			: BOOL;
	// 模式切换
	bModeSwitch			: BOOL;
	// OK、NG信号
	bFindAngle_M1		: BOOL;
	bFindAngle_M2		: BOOL;
END_VAR
]]></Declaration>
    <Implementation>
      <ST><![CDATA[eState := fbCamera.GetState();
CASE eState OF
TCVN_CS_INITIAL,TCVN_CS_OPENING,TCVN_CS_OPENED,TCVN_CS_STARTACQUISITION:
    hr := fbCamera.StartAcquisition();
TCVN_CS_ACQUIRING:
    hr := fbCamera.GetCurrentImage(ipImageIn);
    IF SUCCEEDED(hr) AND ipImageIn <> 0 THEN
		FBProfiler(START:=FALSE , RESET:=TRUE , BUSY=> , DATA=> );
		FBProfiler(START:=TRUE , RESET:=FALSE , BUSY=> , DATA=> );	
		
		hr := F_VN_GetImageWidth(ipImage:=ipImageIn , nWidth:=udWidth , hr );
		hr := F_VN_GetImageHeight(ipImage:=ipImageIn , nHeight:=udHeight , hr );
		// 图像色彩转换
		hr := F_VN_ConvertColorSpace(ipImageIn,ipImageRes,TCVN_CST_GRAY_TO_RGB, hr );
		
		A10_MakeRegion();
		//模式切换
		IF NOT bModeSwitch THEN
			A20_FindContours();
		ELSE
			A20_FindContours();
			A30_LocateEdge();
		END_IF
		// 窗口输出
		hr := F_VN_TransformIntoDisplayableImage(ipImageIn,ipImageInDisp, S_OK );
		hr := F_VN_TransformIntoDisplayableImage(ipImageRegion,ipImageRegionDisp, S_OK );	
		hr := F_VN_TransformIntoDisplayableImage(ipImageRes,ipImageResDisp, S_OK );	
		
		FBProfiler(START:=FALSE , RESET:=FALSE , BUSY=> , DATA=> );
    END_IF
TCVN_CS_ERROR:
    hr := fbCamera.Reset();
END_CASE

// 执行时间统计
lasttime:=   (DWORD_TO_REAL(FBprofiler.DATA.LastExecTime))/1000;
maxtime:=  (DWORD_TO_REAL( FBprofiler.DATA.MaxExecTime))/1000;
mintime:= (DWORD_TO_REAL( FBprofiler.DATA.MinExecTime))/1000;
averagetime:=(DWORD_TO_REAL( FBprofiler.DATA.AverageExecTime))/1000;

]]></ST>
    </Implementation>
    <Action Name="A10_MakeRegion" Id="{7c473c4d-6c5c-41e4-8f9c-935fadaccd93}">
      <Implementation>
        <ST><![CDATA[// 检测区域(ROI)选定
hr := F_VN_CopyImageRegion(
			ipSrcImage:=ipImageIn , 
			nX:=stRoi.nX , 
			nY:=stRoi.nY , 
			nWidth:=stRoi.nWidth , 
			nHeight:=stRoi.nHeight , 
			ipDestImage:=ipImageRegion , 
			hrPrev:=hr );

// C#调试界面实现鼠标选定框, 并由C#传递给 F_VN_CopyImageRegion 进行动态区域选定
IF stDrawRoi.nWidth <= udWidth AND stDrawRoi.nHeight <= udHeight THEN
	hr := F_VN_DrawRectangle_TcVnRectangle_UDINT(stDrawRoi,ipImageRes,aColorRed,5,hr);		
END_IF
		
// 绘制检测区域(ROI)
hr := F_VN_DrawRectangle_TcVnRectangle_UDINT(stRoi,ipImageRes,aColorBlue,5,hr);

// 二值化
hr := F_VN_Threshold(
			ipSrcImage:=ipImageRegion , 
			ipDestImage:=ipImageRegion , 
			fThreshold:=0 , 
			fMaxValue:=255 , 
			eThresholdType:=TCVN_TT_OTSU_BINARY_INV , 
			hrPrev:=hr );
			
// 形态学处理（腐蚀、膨胀）
IF bmorphology THEN	
hr := F_VN_CreateStructuringElement(ipStructimage ,TCVN_SES_RECTANGLE ,udStructElement ,udStructElement ,hr );
hr := F_VN_MorphologicalOperator( ipImageRegion,ipImageRegion ,eOperator ,ipStructimage ,hr );	
END_IF
]]></ST>
      </Implementation>
    </Action>
    <Action Name="A20_FindContours" Id="{4c773fc9-3392-415d-915e-b6433ef8cec0}">
      <Implementation>
        <ST><![CDATA[aOffset1[0] := 	stRoi.nX;
aOffset1[1] := 	stRoi.nY;	
// 获取轮廓列表
hr := F_VN_FindContoursExp(
			ipSrcImage:=ipImageRegion , 
			ipContours:=ipContourList1 , 
			eRetrievalMode:=TCVN_CRM_LIST , 
			eApproximationMethod:=TCVN_CAM_SIMPLE , 
			aOffset:=aOffset1 , 
			hrPrev:=hr );
			
bFindAngle_M1Temp :=FALSE;
// 获取轮廓数量
hr := F_VN_GetNumberOfElements(ipContourList1 , nNumberOfElements , hr );
iDetectNum1:=0;
IF nNumberOfElements>=1 THEN
	FOR iRef1:=0 TO (nNumberOfElements-1) DO
		// 依次获取容器中所有识别的轮廓
		hr := F_VN_GetAt_ITcVnContainer(ipContourList1 ,ipContour1 ,iRef1 , hr );
		// 依次获取容器中所有识别的轮廓面积
		hr := F_VN_ContourArea(ipContour1 , fArea1 , hr );
		IF fArea1>10000 THEN
			IF fArea1>300000 AND fArea1<800000 AND bModeSwitch THEN
				// 获取轮廓质心
				F_VN_ContourCenterOfMass(ipContour1,circleofmass,hr);
				// 绘制轮廓
				hr := F_VN_DrawContours(ipContour1, -1, ipImageRes, aColorGreen, 5, hr);
				// 绘制质心坐标
				hr := F_VN_DrawPointExp(LREAL_TO_UDINT(circleofmass[0]), LREAL_TO_UDINT(circleofmass[1]), ipImageRes, TCVN_DS_X, aColorBlue,5,5,TCVN_LT_4_CONNECTED, hr);
			END_IF	
			// 获取弧度
			hr := F_VN_ContourCircularity(ipContour1,fCircularity,hr);
			// 通过弧度排除中间的圆
			IF(fCircularity< 0.6) THEN
				iDetectNum1:=iDetectNum1+1;
				IF(iDetectNum1 = 1) THEN
					// 获取轮廓质心
					hr := F_VN_ContourCenterOfMass(ipContour1,certerofmass,hr);
					// 提取外接矩形关键特征
					hr := F_VN_EnclosingRectangle(ipContour1,stRectangle,hr);	
					// 绘制外接矩形轮廓
					hr := F_VN_DrawRotatedRectangle(stRectangle,  ipImageRes, aColorGreen, 5, hr);
					IF SUCCEEDED(hr)  THEN
						bFindAngle_M1Temp :=TRUE;
					END_IF
					// 提取矩形宽度、角度特征
					fHideth := stRectangle.stSize.fHeight;
					IF NOT bModeSwitch THEN
						fAngle_M1 := stRectangle.fAngle;
						IF stRectangle.fAngle >90 THEN
							fAngle_M1 := 180-stRectangle.fAngle;
						END_IF
						sAngle_M1:=CONCAT('Mode1 Angle: ' , TO_STRING(fAngle_M1));
						// 绘制模式一的角度结果
						hr := F_VN_PutTextExp(sAngle_M1, ipImageRes, 25, 300, ETcVnFontType.TCVN_FT_HERSHEY_PLAIN,6,aColorGreen,5,TCVN_LT_4_CONNECTED, FALSE, hr);
					END_IF
				END_IF	
			END_IF
		END_IF
	END_FOR
END_IF
bFindAngle_M1 := bFindAngle_M1Temp;
// 内存释放，防止溢出
FW_SafeRelease(ADR(ipContourList1) );
FW_SafeRelease(ADR(ipContour1) );]]></ST>
      </Implementation>
    </Action>
    <Action Name="A30_LocateEdge" Id="{5d93fcf4-8a88-44e4-82f6-fe68fe497a82}">
      <Implementation>
        <ST><![CDATA[// 质心获取起点
aStartPoint[0]:=certerofmass[0];
aStartPoint[1]:=certerofmass[1];

// 等比算法动态获取终点
X1:=circleofmass[0]-certerofmass[0];
Y1:=circleofmass[1]-certerofmass[1];
L:=SQRT(X1*X1+Y1*Y1);
L1:=fHideth;
aEndpoint[0]:=certerofmass[0]-L1*(circleofmass[0]-certerofmass[0])/L;
aEndpoint[1]:=certerofmass[1]-L1*(circleofmass[1]-certerofmass[1])/L;

// 绘制起点与重点坐标
hr := F_VN_DrawPointExp(REAL_TO_UDINT(aStartPoint[0]), REAL_TO_UDINT(aStartPoint[1]), ipImageRes, TCVN_DS_X, aColorRed,5,5,TCVN_LT_4_CONNECTED, hr);
hr := F_VN_DrawPointExp(REAL_TO_UDINT(aEndPoint[0]), REAL_TO_UDINT(aEndPoint[1]), ipImageRes, TCVN_DS_X, aColorRed,5,5,TCVN_LT_4_CONNECTED, hr);

// 寻找起点与终点间的边界
hr := F_VN_LocateEdgeExp(
						ipSrcImage				:= ipImageIn, 
						ipEdgePoints			:= ipEdgePoints, 
						aStartPoint				:= aStartPoint, 
						aEndPoint				:= aEndPoint, 
						eEdgeDirection			:= eDirection, 
						fMinStrength			:= fMinStrength, 
						nSearchLines			:= nSearchLines,
						fSearchLineDist			:= fSearchLineDist, 
						nMaxThickness			:= nMaxThickness, 
						nSubpixelsIterations	:= nSubpixIter, 
						fApproxPrecision		:= 0.0001, 
						eAlgorithm				:= eAlgorithm, 
						hrPrev					:= hr, 
						fAvgStrength			=> fAvgStrength);

// 拟合直线
hr := F_VN_FitLine(ipEdgePoints, aLine, hr);
IF SUCCEEDED(hr) THEN
	bFindAngle_M2 := TRUE;
ELSE
	bFindAngle_M2 := FALSE;
END_IF
// 绘制直线
hr := F_VN_DrawLine_TcVnVector4_LREAL(aLine, ipImageRes, aColorRed, 5, hr);
// 绘制边界点
hr := F_VN_DrawPointsExp(ipEdgePoints, ipImageRes, TCVN_DS_PLUS, aColorBlue, 1, 1, TCVN_LT_8_CONNECTED, hr);	

// 算角度
fAngle_M2:=ABS(ATAN(aLine[1]/aLine[0])/PI*180);
sAngle_M2:=CONCAT('Mode2 Angle: ' , TO_STRING(fAngle_M2));
// 绘制模式二的角度结果
IF bModeSwitch THEN
	hr := F_VN_PutTextExp(sAngle_M2, ipImageRes, 25, 450, ETcVnFontType.TCVN_FT_HERSHEY_PLAIN,6,aColorRed,5, TCVN_LT_4_CONNECTED, FALSE, hr);
END_IF

// 内存释放，防止溢出
FW_SafeRelease(ADR(ipContourList1) );
FW_SafeRelease(ADR(ipContour1) );]]></ST>
      </Implementation>
    </Action>
    <LineIds Name="Prg_Vision">
      <LineId Id="430" Count="8" />
      <LineId Id="776" Count="0" />
      <LineId Id="775" Count="0" />
      <LineId Id="774" Count="0" />
      <LineId Id="439" Count="1" />
      <LineId Id="773" Count="0" />
      <LineId Id="442" Count="0" />
      <LineId Id="688" Count="0" />
      <LineId Id="535" Count="1" />
      <LineId Id="539" Count="0" />
      <LineId Id="613" Count="0" />
      <LineId Id="540" Count="0" />
      <LineId Id="537" Count="0" />
      <LineId Id="445" Count="1" />
      <LineId Id="448" Count="7" />
      <LineId Id="687" Count="0" />
      <LineId Id="456" Count="0" />
      <LineId Id="531" Count="2" />
      <LineId Id="458" Count="1" />
      <LineId Id="103" Count="0" />
    </LineIds>
    <LineIds Name="Prg_Vision.A10_MakeRegion">
      <LineId Id="18" Count="0" />
      <LineId Id="2" Count="7" />
      <LineId Id="40" Count="0" />
      <LineId Id="36" Count="3" />
      <LineId Id="35" Count="0" />
      <LineId Id="19" Count="0" />
      <LineId Id="1" Count="0" />
      <LineId Id="20" Count="0" />
      <LineId Id="11" Count="6" />
      <LineId Id="10" Count="0" />
      <LineId Id="21" Count="0" />
      <LineId Id="30" Count="4" />
      <LineId Id="25" Count="0" />
    </LineIds>
    <LineIds Name="Prg_Vision.A20_FindContours">
      <LineId Id="2" Count="1" />
      <LineId Id="139" Count="0" />
      <LineId Id="4" Count="6" />
      <LineId Id="155" Count="1" />
      <LineId Id="41" Count="0" />
      <LineId Id="85" Count="7" />
      <LineId Id="94" Count="0" />
      <LineId Id="126" Count="1" />
      <LineId Id="136" Count="0" />
      <LineId Id="128" Count="0" />
      <LineId Id="141" Count="0" />
      <LineId Id="129" Count="0" />
      <LineId Id="142" Count="0" />
      <LineId Id="125" Count="0" />
      <LineId Id="95" Count="5" />
      <LineId Id="134" Count="1" />
      <LineId Id="103" Count="3" />
      <LineId Id="146" Count="0" />
      <LineId Id="157" Count="0" />
      <LineId Id="148" Count="0" />
      <LineId Id="107" Count="0" />
      <LineId Id="124" Count="0" />
      <LineId Id="108" Count="0" />
      <LineId Id="158" Count="0" />
      <LineId Id="130" Count="0" />
      <LineId Id="110" Count="3" />
      <LineId Id="143" Count="0" />
      <LineId Id="145" Count="0" />
      <LineId Id="138" Count="0" />
      <LineId Id="116" Count="1" />
      <LineId Id="119" Count="4" />
      <LineId Id="32" Count="0" />
    </LineIds>
    <LineIds Name="Prg_Vision.A30_LocateEdge">
      <LineId Id="134" Count="0" />
      <LineId Id="98" Count="1" />
      <LineId Id="133" Count="0" />
      <LineId Id="48" Count="0" />
      <LineId Id="107" Count="1" />
      <LineId Id="110" Count="3" />
      <LineId Id="135" Count="0" />
      <LineId Id="71" Count="0" />
      <LineId Id="100" Count="0" />
      <LineId Id="72" Count="2" />
      <LineId Id="78" Count="13" />
      <LineId Id="75" Count="0" />
      <LineId Id="137" Count="0" />
      <LineId Id="76" Count="0" />
      <LineId Id="95" Count="0" />
      <LineId Id="150" Count="0" />
      <LineId Id="156" Count="0" />
      <LineId Id="154" Count="1" />
      <LineId Id="153" Count="0" />
      <LineId Id="144" Count="0" />
      <LineId Id="140" Count="0" />
      <LineId Id="145" Count="0" />
      <LineId Id="141" Count="0" />
      <LineId Id="96" Count="0" />
      <LineId Id="77" Count="0" />
      <LineId Id="129" Count="1" />
      <LineId Id="143" Count="0" />
      <LineId Id="146" Count="2" />
      <LineId Id="54" Count="0" />
      <LineId Id="56" Count="1" />
      <LineId Id="2" Count="0" />
    </LineIds>
  </POU>
</TcPlcObject>