在此次实践中,对于Kmeans聚类算法交互式改变簇数的时候,我发现我的button按钮必须点击两次才行,目前还未找出原因。
说明:对于图像的画布显示用Canvas,柱状图、饼图采用的是Echarts模板展示。
需要连接python 服务器,管理员cmd进入当前目录,采用python -m http.server 80000方式建立,后直接在浏览器中输入http://localhost:8000即可。
<!DOCTYPE html>
<!--
1.此次实践实现图片显示以及图片的聚类柱状图饼形图显示。
2.实现图片选择的交互式,仅支持打开JPG图像
3.实现柱状图、条形图切换
4.可视化聚类中心颜色即是柱状图或者饼状图颜色,数量,中心RGB具体值显示需鼠标接触显示
5.实现交互式改变kmeans的簇数,簇数不能为1,实践中没有进行非法处理,不然会出现问题(该过程加载缓慢)最好点击两次
-->
<html>
<head>
<meta charset="utf-8">
<title>Lab01</title>
</head>
<body>
<!--设置div绝对位置-->
<!--border-style:solid;
border-color:blue;-->
<style type = "text/css">
.div2{
height:600px;
background-color:white;
position:absolute;
top:20px;
left:710px;
right:5%;
}
#div4bm{
height:100px;
width:400px;
background-color:white;
position:absolute;
top:470px;
left:590px;
}
#mybutton{
margin-left:50px;
}
#myform{
background-color:white;
margin-left:40%;
}
#container{
height:70%;
width:100%;
}
</style>
<script type="text/javascript" src="echarts.min.js"></script>
<!--script type="text/javascript" src="jquery-1.8.2.min.js"></script-->
<!--
一个画布
画布相关代码参考:https://www.w3school.com.cn/html5/html_5_canvas.asp
-->
<canvas id="myCanvas" width="690" height="457" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<div id="div4bm" >
<!--input[button] 触发 file click事件-->
<input type="button" value="选择文件" id="mybutton" class="mybtn" onclick="Id('file').click();" />
<!--file 隐藏起来 触发onchange事件-->
<input type="file" name="file" accept="image/jpg" id="file" onchange="changeToop(this);" style="display:none;" />
</div>
<div class = "div2">
<div id="container" ></div>
<!--设置Radio按钮,实现柱状图与饼状图的选择-->
<form action="" method="get" id = "myform">
<label>输入簇数:<input name="k_means" type="text" id="myks" style="width:30px;height:20px"/></label>
<label><input name="get" type="button" value="提交(点两次)" onclick = "change_k()"/></label><br/>
<label><input name="charts" type="radio" id="0" onclick = "changechart();" checked/>柱状图 </label>
<label><input name="charts" type="radio" id="1" onclick = "changechart();"/>饼状图</label>
</form>
</div>
<script type="text/javascript">
//响应kmeans簇数k的改变
function change_k()
{
myks = document.getElementById("myks").value;
myload();
}
//响应展示图Radio按钮改变事件函数
function changechart(){
if(document.getElementById("0").checked){ //柱状图
var dom = document.getElementById("container");
var myChart = echarts.init(dom);
var app = {};
var option;
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: [{
type: 'category',
data: colorList,
show:true,
axisTick: {
alignWithLabel: true
},
axisLabel:{
interval:(index,value) =>{return false}//设置X轴数据不显示
}
}],
yAxis: [{
type: 'value',
show:true
}],
grid:{
containLabel:true
},
series: [{
name: '数量',
data: mycnt,
type: 'bar',
barWidth: '50%',
itemStyle: {
//每个柱子的颜色即为colorList数组里的每一项,如果柱子数目多于colorList的长度,则柱子颜色循环使用该数组
color: function (params){
//console.log(params.dataIndex);
//console.log(colorList[params.dataIndex]);
return colorList[params.dataIndex];
}
}
}]
};
if (option && typeof option === 'object') {
myChart.setOption(option);
}
}
else{ //饼状图
var dom = document.getElementById("container");
var myChart = echarts.init(dom);
var app = {};
var option;
var piedata = new Array();
for(var i = 0; i < ks ; i++){
piedata.push({value:mycnt[i],name:colorList[i],itemStyle:{color:colorList[i]}});
}
piedata.sort(function (a, b) {
return a.value - b.value;
});
option = {
xAxis: [{show:false}],
yAxis: [{show:false}],
tooltip: {
trigger: 'item'
},
series: [{
name:"",
labelLine: {show:false},
label:{show:false},
type: 'pie',
radius: '55%',
center: ['50%', '50%'],
data: piedata,
roseType: 'radius',
animationType: 'scale',
animationEasing: 'elasticOut',
animationDelay: function (idx) {
return Math.random() * 200;
}
}]
};
if (option && typeof option === 'object') {
myChart.setOption(option);
}
}
}
//通过JQuery方法响应radio事件
/*$('input[type=radio][name=charts]').change(function () {
// 获取input radio选中值,方法一
var myvalue = $(this).val();
if(myvalue!=check)
changechart(myvalue);
check = myvalue;
console.log(check);
});*/
//加载打开图片并且进行聚类分析
function myload(){
if(ks == myks)
{
var context = document.getElementById('myCanvas').getContext('2d');
context.drawImage(img, 0, 0);
// data中保存了图片所有像素的颜色信息,每个像素的RGBA信息占数组中4位
// RGBA为像素的红色、绿色、蓝色、透明度四个分量
//但此次聚类我分析的只是颜色,故聚类RGB
var data = context.getImageData(0, 0, 690, 457).data;
}
else
{
ks = myks;
colorList.length = 0;
}
//console.log(data);
var cnt = 690*457; //图片中总的像素个数
var point = [];//创建数组保留像素的RGB值
var layout = [];//记录每个簇的点的在point中的标号,就是分在哪个簇
var k = 0;//记录data索引
for(var i = 0; i < cnt; i++)
{
layout[i] =[];
layout[i][0] = 0;
layout[i][1] = 200000;
point[i] = [];
point[i][0] = data[k];
k++;
point[i][1] = data[k];
k++;
point[i][2] = data[k];
k++;
//point[i][3] = data[k];
k++; //透明度A直接跳过
}
//kmeans具体算法
//01.选择初始化的点(随便,或者函数点) 利用随机函数
var centroids = [];
console.log(ks);
for (var i = 0;i < ks; i ++)
{
centroids[i] = [];
var index = Math.round(Math.random()*(cnt));
centroids[i][0] = point[index][0];
centroids[i][1] = point[index][1];
centroids[i][2] = point[index][2];
//centroids[i][3] = point[i][3];
}
var change = true;//标志簇是否改变
while (change==true)
{
change = false;
for ( var i = 0; i < cnt; i++)
{
var mindist = 200000;
var minindex = 0;
//02.每个点找到离得最近的簇
for (var j = 0; j < ks; j++)
{
var dis = Math.sqrt(Math.pow(point[i][0]-centroids[j][0],2)+Math.pow(point[i][1]-centroids[j][1],2)+
Math.pow(point[i][2]-centroids[j][2],2));
if (dis < mindist)
{
mindist = dis;
minindex = j;
}
}
if (layout[i][0] != minindex)
{
change = true;
layout[i][0] = minindex;
layout[i][1] = mindist;
}
}
console.log(change);
//03.更新簇
var mysum = [];
for (var i = 0; i < ks;i++)
{
mysum[i] = [];
mysum[i][0] = 0;
mysum[i][1] = 0;
mysum[i][2] = 0;
//mysum[i][3] = 0;
mycnt[i] = 0;
}
for ( var i = 0; i < cnt;i++)
{
for(var j = 0; j< ks; j++)
{
if(layout[i][0] == j)
{
mysum[j][0] += point[i][0];
mysum[j][1] += point[i][1];
mysum[j][2] += point[i][2];
//mysum[j][3] += point[i][3];
mycnt[j]++;
break;
}
}
}
for(var i = 0; i < ks; i ++)//对于每个坐标每次都需要四舍五入取整数
{
centroids[i][0] = Math.round(mysum[i][0]/mycnt[i]);
centroids[i][1] = Math.round(mysum[i][1]/mycnt[i]);
centroids[i][2] = Math.round(mysum[i][2]/mycnt[i]);
//centroids[i][3] = Math.round(mysum[i][3]/mycnt[i]);
}
}
//记录颜色数组
for (var i = 0; i < ks; i++)
{
colorList[i] = 'rgb('+centroids[i][0]+','+centroids[i][1]+','+centroids[i][2]+')';
console.log(colorList[i]);
}
//聚类图显示
centroids.length=0;
point.length = 0;
layout.length=0;
mysum.length = 0;
data.length = 0;
changechart();
}
function Id(id){
return document.getElementById(id);
}
function changeToop(){
var file = document.getElementById("file");
if(file.value==''){
//设置默认图片
img.src='images/01.jpg';
}
else{
preImg("file");
}
}
//得到打开图片地址
function getFileUrl(fileId) {
var url;
var file = document.getElementById(fileId);
var agent = navigator.userAgent;
if (agent.indexOf("MSIE")>=1) {
url = file.value;
}
else if(agent.indexOf("Firefox")>0) {
url = window.URL.createObjectURL(file.files.item(0));
}
else if(agent.indexOf("Chrome")>0) {
url = window.URL.createObjectURL(file.files.item(0));
}
console.log(url);
return url;
}
//读取图片后预览
function preImg(fileId) {
img.src = getFileUrl(fileId);
console.log(img.src);
img.onload=function(){
myload();
};
myload();
}
//myload();
//初次加载图片
var img = new Image();
img.onload=function(){
myload();
};
var colorList = [];//记录簇的颜色
var mycnt = [];//记录某种簇的个数
var ks = 4; //设置kmeans的簇数 如果画出的图某些位置为空,说明初始点选择出现问题
var myks = 4; //设置输入框的值,只有输入框的值与k相等才需要重新绘图,否则,改变簇数即可
//给予图像初始化地址,刚开始展示该图
img.src='images/01.jpg';
</script>
</body>
</html>
实践效果: