利用canvas识别图片颜色, 存入数据库

1,724 阅读1分钟

颜色识别的核心步骤:
1.选择图片后利用FileReader API将图片转成base64的格式
2.创建一个img标签, 将base64的内容赋值给img标签的src属性
3.调用canvas的drawImage()方法让canvas绘制这张图片
4.调用canvas的getImageData()方法获取某个像素点的颜色值
5.把这个颜色值以rgb和16进制的形式显示出来

运行效果

初始页面就一个显示框和一个选择图片的按钮, 选择图片后, 把鼠标移动到图片上就会显示图片的颜色值, 鼠标移动会跟随显示:

源代码:

<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>图片颜色识别</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            /*align-items: center;*/
            background: white;
            margin-top: 20px;
        }
        
        #color {
            height: 30px;
            line-height: 30px;
            text-align: center;
        }
        
        #canvas {
            border: 1px dashed #000;
        }
        
        .tooltip {
            display: flex;
            justify-content: center;
            align-items: center;
            position: fixed;
            min-width: 150px;
            min-height: 80px;
            margin: 10px;
            padding: 10px 20px;
            border: 1px solid #b3c9ce;
            border-radius: 4px;
            color: #eee;
            font-size: 16px;
            box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
            z-index: 9999999999999999999999;
        }
        
        .fileWrap {
            margin-left: 200px;
        }
    </style>
</head>

<body>


    <div class="wrapper">
        <canvas id="canvas" width="600" height="400"></canvas>
        <br>
        <br>
        <div class="fileWrap">
            <input type="file" onchange="selectImg(this)">
        </div>
        <br>
        <div id="color"></div>
        <br>
    </div>



    <script>
        var canvas = document.getElementById('canvas');
        var canvasW = canvas.width;
        var canvasH = canvas.height;
        var ctx = canvas.getContext('2d');
        var isSelectImg = false;


        function selectImg(that) {
            isSelectImg = true;
            // console.log("that: ", that);
            var file = that.files[0];
            // console.log(file);
            var reader = new FileReader();

            // console.log("reader: ", reader);
            reader.readAsDataURL(file);

            reader.onload = (e) => {
                var img = new Image();
                img.src = reader.result;
                img.onload = () => {
                    // console.log("img.width, img.height: ", img.width, img.height);
                    ctx.clearRect(0, 0, canvasW, canvasH);
                    if (img.width < canvasW && img.height < canvasH) {
                        canvas.height = canvasH;
                        ctx.drawImage(img, 0, 0, img.width, img.height);
                    } else {
                        canvas.height = canvasW * img.height / img.width; // 保持图片不变形
                        ctx.drawImage(img, 0, 0, canvasW, canvas.height);
                    }

                }
            }
        }


        // RGB to Hex
        function colorRGBtoHex(rgb_color) {
            if (/(^( *(rgb)\(( *\d{1,3} *,){2} *\d{1,3} *\)) *$)/.test(rgb_color)) {
                var rgb = rgb_color.split(',');
                var r = parseInt(rgb[0].split('(')[1]);
                var g = parseInt(rgb[1]);
                var b = parseInt(rgb[2].split(')')[0]);
                var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
                return hex;
            }
            return rgb_color;
        }



        // 跟随鼠标移动的提示工具
        function toolTip() {
            let tooltipElem, timer;
            let color = document.getElementById('color');
            document.onmouseover = function(e) {
                var target = e.target;

                if (!isSelectImg) return;
                if (target.tagName !== 'CANVAS') {
                    return;
                }
                tooltipElem = document.createElement('div');
                tooltipElem.className = "tooltip";
                document.body.append(tooltipElem);

                document.onmousemove = function(e) {
                    tooltipElem.style.left = e.clientX + 'px';
                    tooltipElem.style.top = e.clientY + 'px';

                    var x = e.layerX;
                    var y = e.layerY;
                    var pixel = ctx.getImageData(x, y, 1, 1);
                    // console.log("pixel: ", pixel);
                    var data = pixel.data;
                    var rgb =
                        'rgb(' + data[0] +
                        ',' + data[1] +
                        ',' + data[2] +
                        // ',' + (data[3] / 255) + // rgba改成rgb
                        ')';

                    // 函数节流
                    clearInterval(timer);
                    timer = setTimeout(function() {
                    	// 显示颜色值
                        color.style.background = rgb;
                        color.textContent = rgb;
                        tooltipElem.innerText = colorRGBtoHex(rgb);
                        tooltipElem.style.background = rgb;
                    }, 15)

                }

            };

            document.onmouseout = function() {
                if (tooltipElem) {
                    tooltipElem.remove();
                    tooltipElem = null;
                }
                document.onmousemove = null;
                clearInterval(timer)
            }
        }
        toolTip();
    </script>
</body>

</html>

图片颜色识别升级版

升级功能:
1.添加颜色保存到数据库
2.如果图片中的颜色和数据库中已有的颜色一致, 就显示颜色名称

升级后的运行效果

初始界面 选择一张图片(我选的这张图片是从颜色对照中截的图)

新增颜色, 输入颜色名称和颜色值, 然后点击保存:

刷新页面可以看到颜色对照最后有刚刚新增的颜色:

升级后的源代码

后端Node.js启动文件(使用了Express + MySQL):

let my_mysql      = require('mysql');
let express      = require('express');
let path      = require('path');
let conn = my_mysql.createConnection({
    host     : 'localhost',
    user     : 'root',
    password : '123456',
    database : 'color'
});
conn.connect();

var app = express();

const bodyParser = require('body-parser');
app.use(bodyParser.json());//数据JSON类型
app.use(bodyParser.urlencoded({ extended: false }));//解析post请求数据

app.use(express.static(path.join(__dirname, 'public')));



app.get('/getAllColor', async function (req, res) {
    getAllColor(req, res);
})
app.post('/insertColor', async function (req, res) {
    insertColor(req, res)
})




function getAllColor(req, res) {
    conn.query(`SELECT * FROM color`,  function (err, result) {
        if (err) {
            res.send({code: 1, msg: '查询数据库错误'});
            console.log('[SELECT ERROR] - ', err.message);
            return;
        }
        // console.log("result: ", result);
        res.send(result)
    })
}



function insertColor(req, res) {
    console.log(req.body.name, req.body.value);
    var name = req.body.name;
    var value = req.body.value;

    if (name && name.length > 20) return false;
    if (value && value.length > 30) return false;

    let sql = "INSERT INTO color(name, value) VALUES(?,?)";
    let sqlVal = [name, value];
    console.log("sqlVal: ", sqlVal);
    conn.query(sql, sqlVal, function (err, result) {
        if (err) {
            console.log("err: ", err);
            res.send('添加失败')
            return false;
        } else {
            // console.log("result: ", result);
            res.send('添加成功')
            return true;
        }
    })
}



var port = 4006;

console.log("启动服务器的时间: ", new Date().toLocaleString());
console.log("打开浏览器访问: http://localhost:"+port);


app.listen(port);

数据库表设计:

前端首页(使用了Vue + jQuery + Layer):

<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>颜色识别</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            /*align-items: center;*/
            background: white;
            margin-top: 20px;
        }
        
        #color {
            height: 30px;
            line-height: 30px;
            text-align: center;
        }
        
        #canvas {
            border: 1px dashed #000;
        }
        
        .tooltip {
            display: flex;
            justify-content: center;
            align-items: center;
            position: fixed;
            min-width: 150px;
            min-height: 80px;
            margin: 10px;
            padding: 10px 20px;
            border: 1px solid #b3c9ce;
            border-radius: 4px;
            color: #eee;
            font-size: 16px;
            box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);
            z-index: 9999999999999999999999;
        }
        
        .fileWrap {
            margin-left: 200px;
        }
        
        form input {
            height: 25px;
            width: 250px;
        }
        
        form div {
            text-align: center;
            margin-top: 5px;
            line-height: 25px;
            height: 25px;
        }
        
        .left {}
        
        #colorList {
            width: 350px;
            height: 500px;
            overflow: auto;
            margin-left: 20px;
        }
        
        #colorList .name,
        #colorList .value,
        #colorList .colors {
            display: inline-block;
            width: 100px;
            height: 30px;
            overflow: hidden;
            box-sizing: border-box;
        }
    </style>
    <script src="lib/vue.js"></script>
    <script src="lib/jq.js"></script>
    <script src="layer/layer.js"></script>
</head>

<body>


    <div class="left">
        <canvas id="canvas" width="600" height="400"></canvas>
        <br>
        <br>
        <div class="fileWrap">
            <input type="file" onchange="selectImg(this)">
        </div>

        <br>
        <div id="color"></div>
        <br>

        <form id="form">
            <input type="text" id="name" placeholder="颜色名称" autocomplete="off">
            <input type="text" id="value" oninput="realTimeShowColor(this.value)" placeholder="颜色值(可以使用rgb和16进制)" autocomplete="off">
            <button type="button" onclick="insertColor()">保存颜色值</button>
            <div id="realTimeShowColorId"></div>
        </form>

    </div>


    <div class="right">
        <div id="colorList">
            <h2>颜色对照(点击颜色值可以复制)</h2>
            <div>
                <span class="name">颜色名称</span>
                <span class="value">颜色值</span>
                <span class="colors">颜色效果</span>
            </div>
            <div v-for="color in colors">
                <span class="name" @click="copyColorValue">{{color.name}}</span>
                <span class="value" @click="copyColorValue">{{color.value}}</span>
                <span :style="'background:'+ color.value" class="colors"></span>
            </div>
        </div>
    </div>


    <script>
        var alert = layer.msg; // 覆盖浏览器自带的alert
        var canvas = document.getElementById('canvas');
        var realTimeShowColorId = document.getElementById('realTimeShowColorId')
        var canvasW = canvas.width;
        var canvasH = canvas.height;
        var ctx = canvas.getContext('2d');
        var isSelectImg = false;
        // 匹配rgb或者16进制,rgb颜色值大于255会被当做255,因此暂时不做更具体的匹配或判断了
        var regExp = /(^(rgb\((\d{1,3},){2}\d{1,3}\))$)|(^(#[0-9a-fA-F]{6})$)|(^(#[0-9a-fA-F]{3})$)/;


        function selectImg(that) {
            isSelectImg = true;
            // console.log("that: ", that);
            var file = that.files[0];
            // console.log(file);
            var reader = new FileReader();

            console.log("reader: ", reader);
            reader.readAsDataURL(file);

            reader.onload = (e) => {
                var img = new Image();
                img.src = reader.result;
                img.onload = () => {
                    // console.log("img.width, img.height: ", img.width, img.height);
                    ctx.clearRect(0, 0, canvasW, canvasH);
                    if (img.width < canvasW && img.height < canvasH) {
                        canvas.height = canvasH;
                        ctx.drawImage(img, 0, 0, img.width, img.height);
                    } else {
                        canvas.height = canvasW * img.height / img.width; // 保持图片不变形
                        ctx.drawImage(img, 0, 0, canvasW, canvas.height);
                    }

                }
            }
        }


        function insertColor() {
            var name = document.getElementById('name').value
            var value = document.getElementById('value').value

            if (!name) {
                alert('颜色名称不能为空')
                return;
            }

            value = value.replace(/\s/g, ''); // 去掉空格
            if (!regExp.test(value)) {
                alert('颜色值输入有误')
                return;
            }

            $.ajax({
                type: 'post',
                url: '/insertColor',
                data: {
                    name: name,
                    value: value
                },
                success(d) {
                    // console.log("d: ", d);
                    getAllColor();
                    document.forms[0].reset();
                    alert('添加成功')
                }
            })
        }

        var vm = new Vue({
            el: '#colorList',
            data: {
                colors: [],
            },
            methods: {
                copyColorValue(e) {
                    console.log("that: ", e.target.textContent);
                    var input = document.createElement('input');
                    input.value = e.target.textContent;
                    document.body.appendChild(input)
                    input.select();
                    document.execCommand("copy");
                    input.remove();
                    alert('复制成功~')
                }
            },
            created() {
                // document.addEventListener('mousemove', function (e) {
                //     console.log("e: ", e);
                // })
            }
        })

        function getAllColor() {
            $.ajax({
                url: '/getAllColor',
                success(d) {
                    // console.log("getAllColor: ", d);
                    vm.colors = d;
                }
            })
        }
        getAllColor();



        // RGB to Hex
        function colorRGBtoHex(color) {
            if (/(^( *(rgb)\(( *\d{1,3} *,){2} *\d{1,3} *\)) *$)/.test(color)) {
                var rgb = color.split(',');
                var r = parseInt(rgb[0].split('(')[1]);
                var g = parseInt(rgb[1]);
                var b = parseInt(rgb[2].split(')')[0]);
                var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
                return hex;
            }
            return color;
        }


        function toolTip() {
            let tooltipElem, timer;
            let color = document.getElementById('color');
            document.onmouseover = function(e) {
                var target = e.target;

                if (!isSelectImg) return;
                if (target.tagName !== 'CANVAS') {
                    return;
                }
                tooltipElem = document.createElement('div');
                tooltipElem.className = "tooltip";
                document.body.append(tooltipElem);

                document.onmousemove = function(e) {
                    tooltipElem.style.left = e.clientX + 'px';
                    tooltipElem.style.top = e.clientY + 'px';

                    var x = e.layerX;
                    var y = e.layerY;
                    var pixel = ctx.getImageData(x, y, 1, 1);
                    // console.log("pixel: ", pixel);
                    var data = pixel.data;
                    var rgb =
                        'rgb(' + data[0] +
                        ',' + data[1] +
                        ',' + data[2] +
                        // ',' + (data[3] / 255) + // rgba改成rgb
                        ')';
                    color.style.background = rgb;
                    color.textContent = rgb;
                    tooltipElem.innerHTML = rgb;
                    tooltipElem.style.background = rgb;
                    // console.log("rgb: ", rgb);



                    // console.log(vm.colors);
                    let currentElemColor = colorRGBtoHex(rgb);
                    let colors = vm.colors;
                    clearInterval(timer);
                    timer = setTimeout(function() {
                        for (let i = 0; i < colors.length; i++) {
                            var hex = colorRGBtoHex(colors[i].value)
                                // console.log("hex: ", hex);
                                // console.log("colorRGBtoHex(rgb): ", colorRGBtoHex(rgb));
                            if (hex === currentElemColor) {
                                color.textContent = colors[i].name;
                                tooltipElem.innerHTML = colors[i].name;
                                break;
                            }
                        }
                    }, 100)

                }

            };

            document.onmouseout = function() {
                if (tooltipElem) {
                    tooltipElem.remove();
                    tooltipElem = null;
                }
                document.onmousemove = null;
                clearInterval(timer)
            }
        }
        toolTip();


        function realTimeShowColor(value) {
            // console.log("value: ", value);
            value = value.replace(/\s/g, ''); // 去掉空格
            if (!value) {
                realTimeShowColorId.style.background = 'transparent'
                realTimeShowColorId.textContent = ''
                return;
            }
            if (regExp.test(value)) {
                realTimeShowColorId.style.background = value
                realTimeShowColorId.textContent = '颜色效果'
            } else {
                realTimeShowColorId.style.background = 'transparent'
                realTimeShowColorId.textContent = '颜色值不正确'
            }
        }
    </script>
</body>

</html>