使用HTML5的canvas做一个会动的时钟

本文介绍如何使用HTML5的canvas标签绘制动态时钟,包括基本概念、线段绘制、阴影、线连接、变换等应用,最终实现一个会动的时钟。

这次我们的目标是画一个会和时间同步的时钟,不过没有美学感觉,样子丑的厉害。




HTML5支持canvas了,我们可以直接在页面上绘图了,我看了下canvas和GDI+的接口差不多,所以我们先了解些基本的概念和方式,然后来做一个应用吧。

我们做所有的画之情需要一个画布,html的canvas标签就是帮我们声明了一个画布。

<canvas id="mycanvas">
</canvas>

这个默认的画布的大小是300*150,接下来的工作大多就是javaScript来做了。

首先要实例化这个画布

$(
function() {
    var canvas = document.getElementById("mycanvas");
    $.log(canvas.width);
    $.log(canvas.height);
    var context = canvas.getContext("2d");
    $.log(context.canvas);
    $.log(context.fillStyle); //要填充的区域的颜色
    $.log(context.strokeStyle); //要绘制的线条的颜色
    $.log(context.lineCap); //笔帽样式
    $.log(context.lineJoin); //两条连续线段的连接样式
    $.log(context.lineWidth); //线段的宽度
    $.log(context.miterLimit); //斜联接
    $.log(context.shadowColor); //阴影的颜色,默认为#000000,
    $.log(context.shadowOffsetX); //阴影在x方向上的偏移量,默认为0,不受坐标转换的影响。
    $.log(context.shadowOffsetY); //阴影在y方向上的偏移量,默认为0,不受坐标转换的影响。
    $.log(context.shadowBlur); //阴影的模糊度,默认为0,负数值将会被忽略
}
);

上面的结果你可以得到一个大致的想法,就是content可以认为是我们将来作画用的画笔(估计有专业人士对强烈抗议,我直接忽略),canvas就是我们的画布。我们现在的画笔是2D的画笔,换句话说就是画平面几何的画笔。

接下来,就是我们利用这个画笔来学习怎么画了


各种线

$(
function() {
    var canvas = document.getElementById("mycanvas");
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";

    context.beginPath();
    context.lineCap = "butt"; //默认
    context.lineWidth = 10;
    context.moveTo(10, 10);
    context.lineTo(100, 10); //简单的一条线
    context.stroke(); //该方法真正在画布上绘制该线段

    context.beginPath();
    context.lineCap = "round"; //圆形线头
    context.moveTo(10, 30);
    context.lineTo(100, 30);
    context.stroke(); //该方法真正在画布上绘制该线段

    context.beginPath();
    context.lineCap = "square"; //方形线头
    context.moveTo(10, 50);
    context.lineTo(100, 50);
    context.stroke(); //该方法真正在画布上绘制该线段
}
);

各种阴影

$(
function() {
    var canvas = document.getElementById("mycanvas");
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.lineWidth = 10;
    context.shadowColor = "#0000FF";

    context.beginPath();
    context.lineCap = "round";
    context.moveTo(10, 10);
    context.lineTo(100, 10);
    context.shadowOffsetX = 10;
    context.shadowBlur = 10;
    context.stroke();

    context.beginPath();
    context.lineCap = "round";
    context.moveTo(10, 30);
    context.lineTo(100, 30);
    context.shadowOffsetY = 10;
    context.shadowBlur = 10;
    context.stroke();

}
);

各种线∠连接

$(
function() {
    var canvas = document.getElementById("mycanvas");
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.lineWidth = 10;
    context.shadowColor = "#0000FF";

    context.beginPath();
    context.lineJoin = "miter"; //两条线段的外边缘一直扩展到它们相交
    context.moveTo(10, 70);
    context.lineTo(50, 10);
    context.lineTo(80, 70);
    context.stroke();

    context.lineJoin = "bevel"; //以一个斜边进行连接
    context.moveTo(100, 70);
    context.lineTo(140, 10);
    context.lineTo(180, 70);
    context.stroke();

    context.lineJoin = "round"; //:以一个圆弧边进行连接
    context.beginPath();
    context.moveTo(200, 70);
    context.lineTo(240, 10);
    context.lineTo(280, 70);
    context.stroke();
    context.closePath(); //关闭path

}
);

mitre的限定

$(
function() {
    var canvas = document.getElementById("mycanvas");
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.lineWidth = 10;
    context.shadowColor = "#0000FF";


    context.beginPath();
    context.miterLimit = 1; //miterLimit 属性为斜面的长度设置一个上限。
    //只对线条使用设置为 "miter" 的 lineJoin 属性绘制并且两条线段以锐角相交的时候有效
    context.lineJoin = "miter"; //两条线段的外边缘一直扩展到它们相交
    context.moveTo(10, 70);
    context.lineTo(50, 10);
    context.lineTo(80, 70);
    context.stroke();

}
);

各种几何图形

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500; //改变默认高度
    canvas.width = 500;
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.fillStyle = "#AABBCC";
    context.lineWidth = 2;
    context.shadowColor = "#0000FF";

    //矩形
    context.beginPath();
    context.fillRect(10, 10, 50, 50); //实体矩形:x,y,width,height
    context.strokeRect(70, 10, 50, 50)//空心矩形:x,y,width,height

    //context.move(10,100);

    //圆弧:x, y, radius, startAngle, endAngle, anticlockwise
    context.beginPath();
    context.arc(35, 110, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
    context.stroke();
    //context.closePath();

    context.beginPath();
    context.arc(85, 110, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 180, false);
    context.stroke();
    //context.closePath();

    context.beginPath();
    context.arc(135, 110, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 180, true);
    context.stroke();
    //context.closePath();

    context.beginPath();
    context.arc(185, 110, 25, (Math.PI / 180) * 180, (Math.PI / 180) * 360, true);
    context.stroke();
    //context.closePath();

    context.beginPath();
    context.arc(235, 110, 25, (Math.PI / 180) * 90, (Math.PI / 180) * 0, false);
    context.fillStyle = "blue";
    context.fill();
    //context.stroke();
    //context.closePath();

    context.beginPath();
    context.arc(285, 110, 25, (Math.PI / 180) * 180, (Math.PI / 180) * 45, false);
    context.closePath();
    context.stroke();

    context.beginPath();
    context.arc(335, 110, 25, (Math.PI / 180) * 180, (Math.PI / 180) * 45, false);
    context.closePath();
    context.fillStyle = "blue";
    context.fill();
    context.stroke();

    //曲线


    context.beginPath();
    context.moveTo(10, 160); //二次贝塞尔曲线的起始点
    //controlX, controlY, endX, endY
    context.quadraticCurveTo(70, 280, 235, 140);
    context.stroke();
    context.closePath();

    context.beginPath();
    context.moveTo(10, 300); //三次贝塞尔曲线的起始点
    //controlX1, controlY1, controlX2, controlY2, endX, endY
    context.bezierCurveTo(70, 280, 50, 400, 235, 190);
    context.stroke();
    context.closePath();


}
);

各种变换

记得CSS3中的transform不?canvas肯定也有啊

平移

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500; //改变默认高度
    canvas.width = 500;
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.fillStyle = "#AABBCC";
    context.lineWidth = 2;
    context.shadowColor = "#0000FF";
    context.moveTo(10, 10);
    //context.beginPath();
    //context.beginPath();
    context.fillRect(10, 10, 50, 50); //实体矩形:x,y,width,height
    //context.stroke();
    $(canvas).on(
    "click",
    { "context": context },
    function(e) {
        $.log(e.data.context);
        var ctx = e.data.context;
        ctx.translate(10, 10); //再最后的路径点上偏移10*10的位置
        context.fillRect(10, 10, 50, 50);
        context.stroke();
    }
    );
}
);

缩放

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500; //改变默认高度
    canvas.width = 500;
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.fillStyle = "#AABBCC";
    context.lineWidth = 2;
    context.shadowColor = "#0000FF";
    context.moveTo(10, 10);
    //context.beginPath();
    //context.beginPath();
    context.fillRect(10, 10, 50, 50); //实体矩形:x,y,width,height
    //context.stroke();
    $(canvas).on(
    "click",
    { "context": context },
    function(e) {
        $.log(e.data.context);
        var ctx = e.data.context;
        ctx.scale(1.1, 1.1); //在最后的大小基础上缩放倍数 必须是正数
        context.fillRect(10, 10, 50, 50);
        context.stroke();
    }
    );
}
);


旋转

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500; //改变默认高度
    canvas.width = 500;
    var context = canvas.getContext("2d");
    context.strokeStyle = "rgb(255, 0, 0)";
    context.fillStyle = "#AABBCC";
    context.lineWidth = 2;
    context.shadowColor = "#0000FF";
    context.moveTo(10, 10);
    //context.beginPath();
    //context.beginPath();
    context.fillRect(10, 10, 50, 50); //实体矩形:x,y,width,height
    //context.stroke();
    $(canvas).on(
    "click",
    { "context": context },
    function(e) {
        $.log(e.data.context);
        var ctx = e.data.context;
        ctx.rotate((Math.PI / 180) * 10); //旋转的角度,旋转的中心是canvas坐标原点
        context.fillRect(10, 10, 50, 50);
        context.stroke();
    }
    );
}
);

transform,transform的参数比较多,也比较难理解,简单的说transform是最自由的变形方式,下面给出些参考

 //以下两段代码结果一致
 context.transform(1, 0, 0, 1, 10, 10)
 context.translate(10, 10);

 //以下两段代码结果一致
 context.transform(10, 0, 0, 10, 0, 0);
 context.scale(10, 10);

 //以下三段代码结果一致
 context.transform(Math.cos((Math.PI / 180) * 10), Math.sin((Math.PI / 180) * 10), -Math.sin((Math.PI / 180) * 10), Math.cos((Math.PI / 180)) * 10, 0, 0);
 context.transform(-Math.sin((Math.PI/180)*10),Math.cos((Math.PI/180)*10),Math.cos((Math.PI/180)*10),Math.sin((Math.PI/180)*10), 0,0);
 context.rotate(10);

组合

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 100;
    canvas.width = 100;
    var context = canvas.getContext("2d");
    context.fillStyle = "#AABBCC";
    context.fillRect(10, 10, 50, 50);
    //默认 新图形会覆盖在原有内容之上
    context.globalCompositeOperation = "source-over";
    context.fillStyle = "blue";
    context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
    context.fill();
    $("span").html(context.globalCompositeOperation);
    $(canvas).toggle(
    function() {
        canvas.width = 100;
        // 原有内容之下绘制新图形
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "destination-over";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //新图形会仅仅出现与原有内容重叠的部分。其它区域都变成透明的
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "source-in";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //原有内容中与新图形重叠的部分会被保留,其它区域都变成透明的destination-in
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "destination-in";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);

    },
    function() {
        canvas.width = 100;
        //只有新图形中与原有内容不重叠的部分会被绘制出来source-out
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "source-out";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //原有内容中与新图形不重叠的部分会被保留
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "destination-out";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //新图形中与原有内容重叠的部分会被绘制,并覆盖于原有内容之上
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "source-atop";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //原有内容中与新内容重叠的部分会被保留,并会在原有内容之下绘制新图形
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "destination-atop";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //两图形中重叠部分作加色处理
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "lighter";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //两图形中重叠的部分作减色处理darker
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "darker";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //重叠的部分会变成透明
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "xor";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
    },
    function() {
        canvas.width = 100;
        //只有新图形会被保留,其它都被清除掉
        context.clearRect(0, 0, 500, 500);
        context.beginPath();
        context = canvas.getContext("2d");
        context.fillStyle = "#AABBCC";
        context.fillRect(10, 10, 50, 50);
        context.globalCompositeOperation = "copy";
        context.fillStyle = "blue";
        context.arc(70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        $("span").html(context.globalCompositeOperation);
        alert("演示结束");
    }
    );
}
);

字体(看文档说canvas的字体支持CSS样式的描写,但是,我不知道怎么样让canvas的font支持CSS3的在线字体)

<script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>

<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Tangerine">

<script type="text/javascript">
    $.log = function(msg) {
        console.log(msg);
    }

    $(
    function() {
        var canvas = document.getElementById("mycanvas");
        canvas.height = 200;
        canvas.width = 200;
        var context = canvas.getContext("2d");
        context.font = "20px 新宋体";
        context.fillText("这是实心新宋体", 10, 30);
        context.strokeText("这是空心新宋体", 10, 60);
        context.font = "20px Tangerine serif";
        context.fillText("Hello HTML5", 10, 100);
        context.strokeText("Hello HTML5", 10, 150);

    }
    );
</script>

我们尝试写一圈旋转的文字,吧上面的知识点合起来看看效果

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500;
    canvas.width = 500;
    var context = canvas.getContext("2d");
    context.translate(150, 150);
    context.scale(0.7, 0.7);
    context.font = "12px Tahoma";
    for (var i = 0; i < 12; i++) {
        context.fillText((i + 3) % 12 == 0 ? 12 : (i + 3) % 12, 150, 10);
        context.rotate((Math.PI / 6));
    }
}
);


在具体绘制的时候,定位总是让我这样没有空间感的人感觉痛苦,所以我现在canvas上画上很多格子,帮助我进行布局

$(
function() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500;
    canvas.width = 500;
    var context = canvas.getContext("2d");
    context.lineWidth = 1;
    context.strokeStyle = "rgb(211,211,211)";

    for (var i = 0; i < 50; i++) {
        $.log(i);
        context.moveTo(i * 10, 0);
        context.lineTo(i * 10, 500);
        context.stroke();
    }

    for (var i = 0; i < 50; i++) {
        $.log(i);
        context.moveTo(0, i * 10);
        context.lineTo(500, i * 10);
        context.stroke();
    }
}
);

前面的准备工作都完成了,现在我们来综合下,完成一个具有时分秒的会动的钟

$(
function() {
    clock();
    setInterval(clock, 1000);
}
);

function clock() {
    var canvas = document.getElementById("mycanvas");
    canvas.height = 500;
    canvas.width = 500;
    var context = canvas.getContext("2d");

    context.beginPath();
    context.lineWidth = 1;
    context.strokeStyle = "rgb(211,211,211)";

    for (var i = 0; i < 50; i++) {
        $.log(i);
        context.moveTo(i * 10, 0);
        context.lineTo(i * 10, 500);
        context.stroke();
    }

    for (var i = 0; i < 50; i++) {
        $.log(i);
        context.moveTo(0, i * 10);
        context.lineTo(500, i * 10);
        context.stroke();
    }

    context.beginPath();
    context.strokeStyle = "rgb(255,0,0)";
    context.arc(250, 250, 200, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
    context.stroke();

    context.save(); //存储当前画布坐标系状态
    context.beginPath();
    context.font = "14px Tahoma"
    context.translate(255, 255); //将坐标系坐标原点移至图中间
    context.strokeStyle = "#FFFFFF";
    for (var i = 0; i < 12; i++) {
        context.fillText((i + 3) % 12 == 0 ? 12 : (i + 3) % 12, 180, 0);
        context.rotate((Math.PI / 6));
    }
    context.restore();

    context.save();
    context.beginPath();
    context.lineWidth = 5;
    context.translate(255, 255); //将坐标系坐标原点移至图中间
    for (i = 0; i < 60; i++) {
        if (i % 5 != 0) {
            context.beginPath();
            context.moveTo(180, 0);
            context.lineTo(190, 0);
            context.stroke();
        }
        context.rotate(Math.PI / 30);
    }
    context.restore();

    var now = new Date();
    var sec = now.getSeconds();
    var min = now.getMinutes();
    var hr = now.getHours() >= 12 ? now.getHours() - 12 : now.getHours();
    context.save();
    context.translate(255, 255); //将坐标系坐标原点移至图中间
    // - (Math.PI / 6) * 3 是因为0度在3点这里
    context.rotate(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec - (Math.PI / 6) * 3);
    context.lineWidth = 14;
    context.beginPath();
    context.moveTo(-20, 0);
    context.lineTo(150, 0);
    context.stroke();
    context.restore();


    context.save();
    context.translate(255, 255); //将坐标系坐标原点移至图中间
    context.rotate(min * (Math.PI / 30) + (Math.PI / 1800) * sec - (Math.PI / 6) * 3)
    context.lineWidth = 10;
    context.beginPath();
    context.moveTo(-28, 0);
    context.lineTo(160, 0);
    context.stroke();
    context.restore();

    context.save();
    context.translate(255, 255); //将坐标系坐标原点移至图中间
    context.lineWidth = 1;
    context.rotate(sec * (Math.PI / 30) + (Math.PI / 1800) * sec - (Math.PI / 6) * 3)
    context.lineWidth = 10;
    context.beginPath();
    context.moveTo(-28, 0);
    context.lineTo(160, 0);
    context.stroke();
    context.restore();
}


基于 NSFW Model 色情图片识别鉴黄 后面更新视频检测 项目背景: 随着互联网的快速发展,网络上的信息量呈现出爆炸式的增长。然而,互联网上的内容良莠不齐,其中不乏一些不良信息,如色情、暴力等。这些信息对青少年的健康成长和社会风气产生了不良影响。为了净化网络环境,保护青少年免受不良信息的侵害,我国政府加大了对网络内容的监管力度。在此背景下,本项目应运而生,旨在实现对网络图片和视频的自动识别与过滤,助力构建清朗的网络空间。 项目简介: 本项目基于 NSFW(Not Safe For Work)Model,利用深度学习技术对色情图片进行识别与鉴黄。NSFW Model 是一种基于卷积神经网络(CNN)的图像识别模型,通过学习大量的色情图片和非色情图片,能够准确地判断一张图片是否含有色情内容。本项目在 NSFW Model 的基础上,进一步优化了模型结构,提高了识别的准确率和效率。 项目功能: 色情图片识别:用户上传图片后,系统会自动调用 NSFW Model 对图片进行识别,判断图片是否含有色情内容。如果含有色情内容,系统会给出相应的提示,并阻止图片的传播。 视频检测:针对网络视频,本项目采用帧提取技术,将视频分解为一帧帧图片,然后使用 NSFW Model 对这些图片进行识别。如果检测到含有色情内容的图片,系统会给出相应的提示,并阻止视频的传播。 实时监控:本项目可应用于网络直播、短视频平台等场景,实时监控画面内容,一旦检测到含有色情内容的画面,立即进行屏蔽处理,确保网络环境的纯洁。
### 如何在本地部署 NSFW 模型或服务 要在本地环境中成功部署 NSFW(不适宜工作场合内容)检测模型或服务,以下是详细的说明: #### 准备环境 为了确保能够顺利运行模型和服务,需要安装必要的依赖项。这些工具和库包括但不限于以下几类: - **Python 环境**: 推荐使用 Python 3.7 或更高版本。 - **Transformers 库**: 提供加载预训练模型的功能[^1]。 - **PyTorch/TensorFlow**: 支持深度学习框架的计算需求。 - **Pillow (PIL)**: 处理图像文件并将其转换为适合输入模型的形式。 具体命令如下所示: ```bash pip install transformers torch Pillow ``` #### 加载模型与测试 通过 Hugging Face 的 `transformers` 工具包可以直接访问已有的 NSFW 图片分类模型。例如,可以采用名为 `"Falconsai/nsfw_image_detection"` 的公开模型来完成此任务[^1]。 下面是一个简单的代码片段展示如何加载该模型并对单张图片执行预测操作: ```python from PIL import Image from transformers import pipeline def classify_nsfw(image_path): # 打开指定路径下的图片文件 img = Image.open(image_path) # 初始化 image-classification 流水线对象,并指明使用的特定模型名称 classifier = pipeline("image-classification", model="Falconsai/nsfw_image_detection") # 对传入的图片调用流水线方法得到其类别标签及其置信度分数列表形式的结果 result = classifier(img) return result if __name__ == "__main__": test_img_path = "<your_test_image>" output_results = classify_nsfw(test_img_path) print(output_results) ``` 注意替换 `<your_test_image>` 成实际存在的图片绝对或者相对地址字符串值之前再尝试运行以上脚本。 #### 构建 RESTful API 服务 如果希望进一步扩展功能至 Web 应用程序层面,则可考虑利用 Flask/Django 这样的轻量级 web 开发框架构建起支持 HTTP 请求交互的服务端接口。这里给出基于 FastAPI 实现的一个简单例子作为示范用途: ```python import uvicorn from fastapi import FastAPI, File, UploadFile from PIL import Image from io import BytesIO from typing import List from pydantic import BaseModel app = FastAPI() class Prediction(BaseModel): label: str score: float @app.post("/predict/", response_model=List[Prediction]) async def predict(file: UploadFile = File(...)): try: contents = await file.read() pil_image = Image.open(BytesIO(contents)) clf_pipeline = pipeline('image-classification', model='Falconsai/nsfw_image_detection') predictions = clf_pipeline(pil_image) formatted_preds = [{"label": pred['label'], "score": round(pred['score'], 4)} for pred in predictions] return formatted_preds except Exception as e: raise ValueError(f"Error processing uploaded file {e}") if __name__ == '__main__': uvicorn.run(app, host='0.0.0.0', port=8000) ``` 启动服务器之后即可向 `/predict/` 路径发送 POST 请求附带上传待分析的目标图片获取返回结果了。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值