[openCV] 控制摄像头并对画面进行图像处理

Mr_MAO 3小时前 20

opencv库控制摄像头硬件还是很方便的!拍照、录像的功能也很容易就可以实现。

它的强大之处更体现在图像处理方面,镜像、灰度、加文字、滤镜都有大量函数可以直接调用,非常方便,感兴趣可以试一试!


下面是完整cv2库文件,直接import调用即可,我们一起探索opencv的世界吧!

【点击下载】: 

(下载后,解压,放在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)
返回