opencv库控制摄像头硬件还是很方便的!拍照、录像的功能也很容易就可以实现。
它的强大之处更体现在图像处理方面,镜像、灰度、加文字、滤镜都有大量函数可以直接调用,非常方便,感兴趣可以试一试!
下面是完整cv2库文件,直接import调用即可,我们一起探索opencv的世界吧!
【点击下载】:
城通网盘:cv2.7z
(下载后,解压,放在arrdio文件夹的"\lib\"目录下就可以用了)

import win.ui;
import cv2;
/*DSG{{*/
var winform = win.form(text="openCV控制摄像头 -(含画面处理:滤镜+人脸检测)@Mr_MAO";right=1020;bottom=655;bgcolor=0x000000)
winform.add(
btnClose={cls="plus";text="关闭摄像头";left=140;top=535;right=250;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=4};
btnFace={cls="plus";text="人脸检测";left=890;top=535;right=1000;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=11};
btnGray={cls="plus";text="灰度";left=640;top=535;right=750;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=8};
btnMirror={cls="plus";text="镜像";left=515;top=535;right=625;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=7};
btnOpen={cls="plus";text="打开摄像头";left=15;top=535;right=125;bottom=582;border={radius=8};color=0xFFFFFF;font=LOGFONT(h=-13);notify=1;z=3};
btnPhoto={cls="plus";text="拍照";left=265;top=535;right=375;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=5};
btnRecord={cls="plus";text="录像";left=390;top=535;right=500;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=6};
btnText={cls="plus";text="文字";left=765;top=535;right=875;bottom=582;border={radius=8};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-13);notify=1;z=9};
cboFilter={cls="combobox";left=80;top=603;right=200;bottom=629;disabled=1;edge=1;items={"原图","复古","黑白","素描","浮雕","模糊","锐化","边缘","反色"};mode="dropdownlist";z=13};
lblFPS={cls="plus";text="FPS: --";left=864;top=600;right=1000;bottom=630;align="left";color=0x888888;font=LOGFONT(h=-13);z=12};
lblFilter={cls="bkplus";text="滤镜:";left=16;top=603;right=66;bottom=631;align="right";color=0xCCCCCC;font=LOGFONT(h=-13);z=14};
lblStatus={cls="plus";text="状态: 摄像头未打开";left=352;top=600;right=497;bottom=630;align="left";color=0xAAAAAA;font=LOGFONT(h=-13);z=10};
panelBottom={cls="bkplus";left=0;top=520;right=1020;bottom=656;bgcolor=0x2D2D2D;db=1;dl=1;dr=1;z=2};
plusCamera={cls="plus";left=0;top=0;right=1020;bottom=520;bgcolor=0x202020;db=1;dl=1;dr=1;dt=1;z=1}
)
/*}}*/
// ========== 状态变量 ==========
var mirrorEnabled = false;
var grayEnabled = false;
var textEnabled = false;
var faceEnabled = false;
var recording = false;
var filterIndex = 1; // 1=原图
var cap = null;
var videoWriter = null;
var faceCascade = null;
var lastFaces = [];
var faceFrameCount = 0;
var frameCount = 0;
var fpsTimer = time.tick();
var lastFPS = 0;
var busy = false;
var PIXEL_FORMAT = 0x26200A;
// ========== 录像控制 ==========
var stopRecording = function(){
recording = false;
if(videoWriter){
videoWriter.release();
videoWriter = null;
}
winform.lblStatus.text = "状态: 录像已停止";
}
var startRecording = function(w, h){
var desktop = io.getSpecial(0);
var tm = time();
var filename = string.format("video_%s.avi", tostring(tm, "%Y%m%d_%H%M%S"));
var path = io.joinpath(desktop, filename);
var fourcc = cv2.VideoWriter_fourcc("MJPG");
videoWriter = cv2.VideoWriter(path, fourcc, 20.0, [w, h], true);
if(videoWriter.isOpened()){
recording = true;
winform.lblStatus.text = "状态: 正在录像...";
}
else {
videoWriter.release();
videoWriter = null;
winform.lblStatus.text = "状态: 录像初始化失败";
}
}
// ========== 滤镜函数 ==========
var applyFilter = function(frame, idx){
select(idx){
case 1 { // 原图
return frame;
}
case 2 { // 复古
var cm = cv2.applyColorMap(frame, cv2.COLORMAP_PINK);
var result = cv2.addWeighted(frame, 0.35, cm, 0.65, 0);
cm.release();
return result;
}
case 3 { // 黑白
var gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
var result = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR);
gray.release();
return result;
}
case 4 { // 素描
var gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
var blur = cv2.GaussianBlur(gray, [5,5], 0);
var edges = cv2.Canny(blur, 50, 150);
var inv = cv2.bitwise_not(edges);
var result = cv2.cvtColor(inv, cv2.COLOR_GRAY2BGR);
gray.release(); blur.release(); edges.release(); inv.release();
return result;
}
case 5 { // 浮雕
var gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
var k = cv2.Mat(3, 3, cv2.CV_32FC1);
k.set(0, 0, -2.0); k.set(0, 1, -1.0); k.set(0, 2, 0.0);
k.set(1, 0, -1.0); k.set(1, 1, 1.0); k.set(1, 2, 1.0);
k.set(2, 0, 0.0); k.set(2, 1, 1.0); k.set(2, 2, 2.0);
var embossed = cv2.filter2D(gray, cv2.CV_32F, k);
k.release();
var shifted = cv2.convertScaleAbs(embossed, 1.0, 128.0);
embossed.release();
var result = cv2.cvtColor(shifted, cv2.COLOR_GRAY2BGR);
gray.release(); shifted.release();
return result;
}
case 6 { // 模糊
var result = cv2.GaussianBlur(frame, [15,15], 0);
return result;
}
case 7 { // 锐化
var blur = cv2.GaussianBlur(frame, [5,5], 1.5);
var result = cv2.addWeighted(frame, 2.0, blur, -1.0, 0);
blur.release();
return result;
}
case 8 { // 边缘检测
var gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
var blur = cv2.GaussianBlur(gray, [5,5], 0);
var edges = cv2.Canny(blur, 80, 200);
var result = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR);
gray.release(); blur.release(); edges.release();
return result;
}
case 9 { // 反色
var result = cv2.bitwise_not(frame);
return result;
}
}
return frame;
}
// ========== 人脸检测 ==========
var detectFaces = function(frame){
faceFrameCount++;
if(faceFrameCount % 4 != 0) return;
if(!faceCascade) return;
var scale = 0.4;
var small = cv2.resize(frame, [0, 0], scale, scale);
var smallGray = cv2.cvtColor(small, cv2.COLOR_BGR2GRAY);
var faces = faceCascade.DetectMultiScale(smallGray.net.Value, 1.1, 3, 0, OpenCvSharp.Size(30, 30));
lastFaces = [];
var len = faces ? tonumber(faces.Length) : 0;
if(len > 0){
var scaleX = frame.width / small.width;
var scaleY = frame.height / small.height;
for(i=1; len; 1){
var f = faces[i];
table.push(lastFaces, {
x = math.round(f.X * scaleX);
y = math.round(f.Y * scaleY);
w = math.round(f.Width * scaleX);
h = math.round(f.Height * scaleY);
});
}
}
smallGray.release();
small.release();
}
// ========== 在帧上绘制人脸框 ==========
var drawFaceRects = function(frame){
for(i=1; #lastFaces; 1){
var f = lastFaces[i];
cv2.rectangle(frame, [f.x, f.y], [f.x+f.w, f.y+f.h], [0, 255, 0], 2);
}
}
// ========== 循环采集画面 ==========
var cameraLoop = function(){
if(busy || !cap || !cap.isOpened()) return;
busy = true;
var ok, frame = cap.read();
if(!ok){
busy = false;
return;
}
// 1. 镜像
if(mirrorEnabled){
var flipped = cv2.flip(frame, 1);
frame.release();
frame = flipped;
}
// 2. 灰度
if(filterIndex != 1){
var filtered = applyFilter(frame, filterIndex);
if(filtered !== frame){
frame.release();
frame = filtered;
}
}
elseif(grayEnabled){
var gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
var grayBgr = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR);
gray.release();
frame.release();
frame = grayBgr;
}
// 3. 人脸检测 + 绘制方框
if(faceEnabled){
detectFaces(frame);
drawFaceRects(frame);
}
// 4. 文字水印
if(textEnabled){
var ts = tostring(time(), "%Y-%m-%d %H:%M:%S");
cv2.putText(frame, ts, [10, 30], cv2.FONT_HERSHEY_SIMPLEX, 0.7, [0, 255, 0], 2);
}
// 5. 录像 REC 标记
if(recording){
cv2.putText(frame, "REC", [frame.width - 60, 30], cv2.FONT_HERSHEY_SIMPLEX, 0.7, [0, 0, 255], 2);
if(videoWriter && videoWriter.isOpened()) videoWriter.write(frame);
}
// 6. BGRA 转为 gdip.bitmap 显示
var bgra = cv2.cvtColor(frame, cv2.COLOR_BGR2BGRA);
var pixelBuf = bgra.toBytes();
var stride = bgra.width * 4;
var bmp = gdip.bitmap(bgra.width, bgra.height, PIXEL_FORMAT, pixelBuf, stride);
if(bmp){
winform.plusCamera.background = bmp;
}
bgra.release();
frame.release();
frameCount++;
var now = time.tick();
if(now - fpsTimer >= 1000){
lastFPS = frameCount;
frameCount = 0;
fpsTimer = now;
winform.lblFPS.text = "FPS: " + lastFPS;
}
busy = false;
}
var openCamera = function(){
if(cap && cap.isOpened()) return true;
cap = cv2.VideoCapture(0);
if(!cap || !cap.isOpened()){
winform.lblStatus.text = "状态: 摄像头无法打开";
cap = null;
return false;
}
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640);
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480);
// 初始化人脸检测级联分类器
var xmlPath = io.fullpath("~/lib/cv2/.res/haarcascade_frontalface_default.xml");
if(io.exist(xmlPath)){
faceCascade = OpenCvSharp.CascadeClassifier(xmlPath);
}
winform.btnClose.disabled = false; winform.btnPhoto.disabled = false;
winform.btnRecord.disabled = false; winform.cboFilter.disabled = false;
winform.btnMirror.disabled = false; winform.btnGray.disabled = false;
winform.btnText.disabled = false; winform.btnFace.disabled = false;
winform.lblStatus.text = "状态: 就绪";
winform.lblFPS.text = "FPS: 0";
frameCount = 0;
faceFrameCount = 0;
lastFaces = [];
fpsTimer = time.tick();
winform.setInterval(cameraLoop, 30);
return true;
}
var closeCamera = function(){
stopRecording();
if(cap){
cap.release();
cap = null;
}
if(faceCascade){
faceCascade.Dispose();
faceCascade = null;
}
winform.plusCamera.background = null;
winform.cboFilter.disabled = true; winform.btnClose.disabled = true;
winform.btnPhoto.disabled = true; winform.btnRecord.disabled = true;
winform.btnMirror.disabled = true; winform.btnGray.disabled = true;
winform.btnText.disabled = true; winform.btnFace.disabled = true;
winform.lblStatus.text = "状态: 摄像头已关闭";
winform.lblFPS.text = "FPS: --";
}
// ========== 拍照 ==========
var takePhoto = function(){
if(!cap || !cap.isOpened()) return;
var ok, frame = cap.read();
if(!ok) return;
if(mirrorEnabled){
var flipped = cv2.flip(frame, 1);
frame.release();
frame = flipped;
}
// 滤镜 / 灰度
if(filterIndex != 1){
var filtered = applyFilter(frame, filterIndex);
if(filtered !== frame){
frame.release();
frame = filtered;
}
}
elseif(grayEnabled){
var gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
var grayBgr = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR);
gray.release();
frame.release();
frame = grayBgr;
}
// 拍照时强制实时检测人脸并绘制
if(faceEnabled && faceCascade){
var scale = 0.4;
var small = cv2.resize(frame, [0, 0], scale, scale);
var smallGray = cv2.cvtColor(small, cv2.COLOR_BGR2GRAY);
var faces = faceCascade.DetectMultiScale(smallGray.net.Value, 1.1, 3, 0, OpenCvSharp.Size(30, 30));
var len = faces ? tonumber(faces.Length) : 0;
if(len > 0){
var scaleX = frame.width / small.width;
var scaleY = frame.height / small.height;
for(i=1; len; 1){
var f = faces[i];
var x = math.round(f.X * scaleX);
var y = math.round(f.Y * scaleY);
var w = math.round(f.Width * scaleX);
var h = math.round(f.Height * scaleY);
cv2.rectangle(frame, [x, y], [x+w, y+h], [0, 255, 0], 2);
}
}
smallGray.release();
small.release();
}
if(textEnabled){
var ts = tostring(time(), "%Y-%m-%d %H:%M:%S");
cv2.putText(frame, ts, [10, 30], cv2.FONT_HERSHEY_SIMPLEX, 0.7, [0, 255, 0], 2);
}
var path = io.joinpath(io.getSpecial(0), "photo_" + tostring(time(), "%Y%m%d_%H%M%S") + ".jpg");
var r = cv2.imwrite(path, frame);
winform.lblStatus.text = r ? "状态: 照片已保存" : "状态: 保存失败";
frame.release();
}
// ========== 设置按钮激活/非激活状态的样式 ==========
var setToggleStyle = function(ctrl, active){
if(active){
ctrl.skin({
background={default=0xFF2E8B57;hover=0xFF3CB371;active=0xFF006400};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
}
else {
ctrl.skin({
background={default=0xFF4A4A4A;hover=0xFF5A5A5A;active=0xFF6A6A6A};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
}
}
// ========== 按钮样式 ==========
winform.btnOpen.skin({
background={default=0xFF2E8B57;hover=0xFF3CB371;active=0xFF006400};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
winform.btnClose.skin({
background={default=0xFFCC3333;hover=0xFFDD4444;active=0xFF992222};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
winform.btnPhoto.skin({
background={default=0xFF3A7BD5;hover=0xFF4A8BE5;active=0xFF2A6BC5};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
winform.btnRecord.skin({
background={default=0xFF3A7BD5;hover=0xFF4A8BE5;active=0xFF2A6BC5};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
// 切换按钮初始样式
setToggleStyle(winform.btnMirror, mirrorEnabled);
setToggleStyle(winform.btnGray, grayEnabled);
setToggleStyle(winform.btnText, textEnabled);
setToggleStyle(winform.btnFace, faceEnabled);
// ========== 按钮事件 ==========
winform.btnOpen.oncommand = function(){ openCamera(); }
winform.btnClose.oncommand = function(){ closeCamera(); }
winform.btnPhoto.oncommand = function(){ takePhoto(); }
winform.btnRecord.oncommand = function(){
if(!cap || !cap.isOpened()) return;
if(!recording){
var ok, frame = cap.read();
if(!ok) return;
startRecording(frame.width, frame.height);
frame.release();
if(recording){
owner.skin({
background={default=0xFFCC3333;hover=0xFFDD4444;active=0xFF992222};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
owner.text = "停止录像";
}
}
else {
stopRecording();
owner.skin({
background={default=0xFF3A7BD5;hover=0xFF4A8BE5;active=0xFF2A6BC5};
color={default=0xFFFFFFFF;hover=0xFFFFFFFF;active=0xFFFFFFFF}
});
owner.text = "录像";
}
}
winform.btnMirror.oncommand = function(){
mirrorEnabled = !mirrorEnabled;
setToggleStyle(owner, mirrorEnabled);
}
winform.btnGray.oncommand = function(){
grayEnabled = !grayEnabled;
setToggleStyle(owner, grayEnabled);
}
winform.btnText.oncommand = function(){
textEnabled = !textEnabled;
setToggleStyle(owner, textEnabled);
}
winform.btnFace.oncommand = function(){
faceEnabled = !faceEnabled;
setToggleStyle(owner, faceEnabled);
if(!faceEnabled){
lastFaces = [];
}
}
// ========== 滤镜下拉框事件 ==========
winform.cboFilter.oncommand = function(id, event){
if(event == 1/*CBN_SELCHANGE*/){
filterIndex = owner.selIndex;
}
}
winform.show();
win.loopMessage();
最新回复 (0)