571 lines
20 KiB
HTML
571 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" >
|
|
<head>
|
|
<link rel="stylesheet" href="../static/element.css">
|
|
<script src="../static/vue.js"> </script>
|
|
<script src="../static/element.js"> </script>
|
|
|
|
<title>公共安全技术研究中心算法集</title>
|
|
<style lang="scss">
|
|
.upload-container {
|
|
background-color: #d3d3d3; /* Grey background */
|
|
padding: 20px;
|
|
border-radius: 15px;
|
|
display: flex;
|
|
justify-content: space-around; /* Space between form and button */
|
|
align-items: center; /* Vertically center */
|
|
width: 80%;
|
|
margin: 20px auto; /* Centering the div */
|
|
}
|
|
.result-container {
|
|
background-color: hsl(125, 85%, 33%); /* Grey background */
|
|
padding: 20px;
|
|
border-radius: 15px;
|
|
display: flex;
|
|
justify-content: space-around; /* Space between form and button */
|
|
align-items: center; /* Vertically center */
|
|
width: 80%;
|
|
margin: 20px auto; /* Centering the div */
|
|
}
|
|
|
|
.upload-btn-wrapper {
|
|
background-color: #4CAF50; /* Green background */
|
|
border: none;
|
|
color: white;
|
|
padding: 8px 16px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
display: inline-block;
|
|
font-size: 16px;
|
|
margin: 4px 2px;
|
|
cursor: pointer;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.webcam-btn {
|
|
background-color: #4CAF50; /* Green background */
|
|
border: none;
|
|
color: white;
|
|
padding: 8px 16px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.file-input {
|
|
display: none; /* Hide the default file input */
|
|
}
|
|
.res-input {
|
|
width: 100;
|
|
font-size: large;
|
|
|
|
|
|
}
|
|
|
|
.custom-file-label {
|
|
border: 2px solid whitesmoke;
|
|
display: inline-block;
|
|
padding: 6px 12px;
|
|
cursor: pointer;
|
|
}
|
|
.video-file-label {
|
|
border: 2px solid whitesmoke;
|
|
display: inline-block;
|
|
padding: 6px 12px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
|
|
.video-container {
|
|
width: 70%;
|
|
margin: 20px auto;
|
|
background-color: #f0f0f0;
|
|
border-radius: 15px;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
|
|
text-align: center;
|
|
padding: 15px;
|
|
margin-left: 190px;
|
|
height: 100%;
|
|
}
|
|
.left-column {
|
|
flex: 3;
|
|
background-color: hsl(0, 24%, 96%); /* Grey background */
|
|
border-radius: 15px;
|
|
justify-content: space-around; /* Space between form and button */
|
|
align-items: center; /* Vertically center */
|
|
}
|
|
|
|
.right-column {
|
|
flex: 1;
|
|
background-color: #d3d3d3; /* Grey background */
|
|
border-radius: 15px;
|
|
justify-content: space-around; /* Space between form and button */
|
|
align-items: center; /* Vertically center */
|
|
|
|
}
|
|
|
|
#bg {
|
|
max-width: 100%;
|
|
height: auto;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.status-message {
|
|
text-align: center;
|
|
margin-top: 20px;
|
|
font-size: 18px;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
width: 80%;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
|
|
.success {
|
|
color: #155724; /* Dark green */
|
|
background-color: #d4edda; /* Light green */
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
|
|
.error {
|
|
color: #721c24; /* Dark red */
|
|
background-color: #f8d7da; /* Light red */
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
|
|
.webcam-btn-active {
|
|
background-color: #dc3545; /* Red background */
|
|
}
|
|
|
|
.mode-txet {
|
|
background-color: #1208a7; /* Green background */
|
|
border: none;
|
|
color: white;
|
|
padding: 8px 16px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.select-text {
|
|
border: none;
|
|
color: rgb(8, 8, 8);
|
|
padding: 8px 16px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
border-radius: 4px;
|
|
}
|
|
.left-align {
|
|
text-align: left;
|
|
}
|
|
.el-row {
|
|
margin-bottom: 20px;
|
|
|
|
}
|
|
.el-col {
|
|
border-radius: 4px;
|
|
}
|
|
.bg-purple-dark {
|
|
background: #99a9bf;
|
|
}
|
|
.bg-purple {
|
|
background: #d3dce6;
|
|
}
|
|
.bg-purple-light {
|
|
font-size: 50px;
|
|
background: #e5e9f2;
|
|
}
|
|
.grid-content {
|
|
border-radius: 4px;
|
|
min-height: 36px;
|
|
}
|
|
.row-bg {
|
|
padding: 10px 0;
|
|
background-color: #f9fafc;
|
|
}
|
|
.custom-input .el-input__inner {
|
|
|
|
height: 60px; /* 设置输入部分的高度 */
|
|
}
|
|
.custom-input {
|
|
|
|
height: 60px; /* 设置输入部分的高度 */
|
|
font-size: 50px;
|
|
}
|
|
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<div class="upload-container" id="el-up">
|
|
<h2>算法配置</h2>
|
|
<form id="selection-form">
|
|
<button type="button" class="mode-txet">算法选择</button>
|
|
<el-select v-model="selectValue" placeholder="行人检测">
|
|
<el-option
|
|
v-for="item in options"
|
|
:key="item[0]"
|
|
:label="item[1]"
|
|
:value="item[1]">
|
|
</el-option>
|
|
</el-select>
|
|
<el-button type="success" @click="submitForm"> 确定</el-button>
|
|
</form>
|
|
|
|
<form id="selection-form">
|
|
<button type="button" class="mode-txet">摄像机选择</button>
|
|
<select id="dropdown_cam" name="dropdown_cam" class="select-text">
|
|
|
|
</select>
|
|
<button type="button" onclick="submitCamera()" class="webcam-btn">确定</button>
|
|
|
|
</form>
|
|
|
|
<form id="selection-form">
|
|
<button type="button" class="mode-txet">绊线选择</button>
|
|
<select id="dropdown" name="dropdown" class="select-text">
|
|
<option value="绊线1">绊线1</option>
|
|
<option value="绊线2">绊线2</option>
|
|
<option value="绊线3">绊线3</option>
|
|
</select>
|
|
<button type="button" onclick="submitForm()" class="webcam-btn">确定</button>
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<div class="upload-container">
|
|
<h2>输入源选择</h2>
|
|
<form id="upload-form" enctype="multipart/form-data">
|
|
<button type="button" class="mode-txet">模式1</button>
|
|
<label class="custom-file-label" for="file-input">选择图片或视频</label>
|
|
<input id="file-input" class="file-input" type="file" name="file">
|
|
<!-- <button type="button" onclick="uploadFile()" class="upload-btn-wrapper">上传</button> -->
|
|
<el-button @click="uploadFile">上传</el-button>
|
|
</form>
|
|
<div class="left-align">
|
|
|
|
<el-row>
|
|
<button type="button" class="mode-txet">模式2</button>
|
|
<el-button ref="webcam-btn" id="turn-on-webcam-btn" @click="openWebcam()" type="primary">打开相机RTSP</el-button>
|
|
|
|
|
|
</el-row>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
<div id="upload-status">
|
|
|
|
</div>
|
|
<div id="message-container" class="status-message"></div>
|
|
|
|
<!-- 将检测结果显示在页面上 -->
|
|
<div>
|
|
<el-row>
|
|
<el-col :span="16"><div class="grid-content bg-purple">
|
|
<img id="bg" src="{{url_for('video_feed')}}">
|
|
</div></el-col>
|
|
<el-col :span="8"><div class="grid-content bg-purple-light">
|
|
检测到的人数为:<el-input v-model="numPeople" class="custom-input" ></el-input>
|
|
|
|
</div></el-col>
|
|
</el-row>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
new Vue({
|
|
el: '#app',
|
|
data() {
|
|
return{
|
|
numPeople:100,
|
|
accuracy: 1,
|
|
getUseCam: false,
|
|
getUseVideo: false,
|
|
options: [],
|
|
selectValue: ''
|
|
}
|
|
},
|
|
created() {
|
|
this.fetchAlgData()
|
|
|
|
},
|
|
methods: {
|
|
openWebcam() {
|
|
var webcamBtn = document.getElementById('turn-on-webcam-btn');
|
|
var videoFeed = document.getElementById('bg');
|
|
var formData = new FormData();
|
|
var that = this
|
|
|
|
|
|
if (webcamBtn.innerHTML === '打开相机RTSP') {
|
|
that.getUseCam = true;
|
|
//console.log(that.getUseCam+'相机RTSP');
|
|
|
|
videoFeed.src = "{{ url_for('use_webcam') }}?" + new Date().getTime();
|
|
webcamBtn.innerHTML = '关闭相机RTSP';
|
|
webcamBtn.classList.add('webcam-btn-active');
|
|
|
|
} else {
|
|
that.getUseCam = false;
|
|
videoFeed.src = ''; // Turn off the webcam
|
|
that.numPeople = 100;
|
|
webcamBtn.innerHTML = '相机RTSP';
|
|
//console.log(that.getUseCam+'相机1RTSP');
|
|
webcamBtn.classList.remove('webcam-btn-active');
|
|
|
|
}
|
|
|
|
if(that.getUseCam == true){
|
|
fetch('/use_webcam')
|
|
.then(response => {
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
|
function readStream() {
|
|
let buffer = '';
|
|
let jsonEndIndex; // 声明 jsonEndIndex 变量
|
|
|
|
return reader.read().then(({ done, value }) => {
|
|
if (done || that.getUseCam == false) {
|
|
that.numPeople = 100;
|
|
console.log('Stream ended');
|
|
return;
|
|
}
|
|
|
|
buffer += decoder.decode(value);
|
|
|
|
const numPeopleIndex = buffer.indexOf('--num_people');
|
|
|
|
if (numPeopleIndex !== -1) {
|
|
const jsonStartIndex = buffer.indexOf('\r\n\r\n', numPeopleIndex) + 4;
|
|
jsonEndIndex = buffer.indexOf('\r\n\r\n', jsonStartIndex); // 移除 const
|
|
|
|
const json = buffer.slice(jsonStartIndex, jsonEndIndex);
|
|
|
|
try {
|
|
//console.log(JSON.parse(json))
|
|
const { num_people } = JSON.parse(json);
|
|
that.numPeople = num_people
|
|
//console.log(that.numPeople);
|
|
} catch (error) {
|
|
that.numPeople = 0
|
|
console.error('Invalid JSON:', error);
|
|
}
|
|
}
|
|
// 剩余的数据继续读取
|
|
buffer = buffer.slice(jsonEndIndex + 4);
|
|
return readStream();
|
|
});
|
|
}
|
|
|
|
return readStream();
|
|
})
|
|
.catch(error => console.error('Error:', error)
|
|
);
|
|
|
|
|
|
}
|
|
},
|
|
|
|
async fetchAlgData() {
|
|
const response = await fetch('/getSqlAlg', {method:'Get'});
|
|
const data = await response.json();
|
|
this.options = data;
|
|
this.selectValue = this.options[0][1];
|
|
|
|
},
|
|
submitForm() {
|
|
// console.log(this.selectValue);
|
|
var formData = new FormData();
|
|
formData.append('selectAlgorithm',this.selectValue);
|
|
fetch('/getSelectAlgorithm', {
|
|
method: 'POST',
|
|
body: formData
|
|
}).then(response => {
|
|
|
|
if (!response.ok) {
|
|
return response.text().then(text => { throw new Error(text || 'Response not OK') });
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
uploadFile() {
|
|
var formData = new FormData();
|
|
var fileInput = document.getElementById('file-input');
|
|
var that = this;
|
|
if (fileInput.files.length > 0) {
|
|
formData.append('file', fileInput.files[0]);
|
|
|
|
fetch('/upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
return response.text().then(text => { throw new Error(text || 'Response not OK') });
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(result => {
|
|
showMessage(result.message);
|
|
if (result.message.includes('uploaded successfully')) {
|
|
that.loadVideoFeed();
|
|
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showMessage('Upload failed: ' + error.message, true);
|
|
});
|
|
|
|
} else {
|
|
showMessage('Please select a file first.', true);
|
|
}
|
|
},
|
|
loadVideoFeed() {
|
|
var that = this;
|
|
var videoFeed = document.getElementById('bg');
|
|
videoFeed.src = "{{ url_for('video_feed') }}?" + new Date().getTime();
|
|
that.getUseVideo = true;
|
|
// console.log(that.getUseVideo)
|
|
if(that.getUseVideo == true){
|
|
fetch('/video_feed')
|
|
.then(response => {
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
|
function readStream() {
|
|
let buffer = '';
|
|
let jsonEndIndex; // 声明 jsonEndIndex 变量
|
|
|
|
return reader.read().then(({ done, value }) => {
|
|
if (done || that.getUseVideo == false) {
|
|
that.numPeople = 100;
|
|
console.log('Stream ended');
|
|
return;
|
|
}
|
|
|
|
buffer += decoder.decode(value);
|
|
|
|
const numPeopleIndex = buffer.indexOf('--num_people');
|
|
|
|
if (numPeopleIndex !== -1) {
|
|
const jsonStartIndex = buffer.indexOf('\r\n\r\n', numPeopleIndex) + 4;
|
|
jsonEndIndex = buffer.indexOf('\r\n\r\n', jsonStartIndex); // 移除 const
|
|
|
|
const json = buffer.slice(jsonStartIndex, jsonEndIndex);
|
|
|
|
try {
|
|
//console.log(JSON.parse(json))
|
|
const { num_people } = JSON.parse(json);
|
|
that.numPeople = num_people
|
|
//console.log(that.numPeople);
|
|
} catch (error) {
|
|
that.numPeople = 0
|
|
console.error('Invalid JSON:', error);
|
|
}
|
|
}
|
|
// 剩余的数据继续读取
|
|
buffer = buffer.slice(jsonEndIndex + 4);
|
|
return readStream();
|
|
});
|
|
}
|
|
|
|
return readStream();
|
|
})
|
|
.catch(error => console.error('Error:', error)
|
|
);
|
|
}
|
|
}
|
|
},
|
|
})
|
|
// Update the label text with the selected file name
|
|
document.getElementById('file-input').onchange = function () {
|
|
document.querySelector('.custom-file-label').textContent = this.files[0].name;
|
|
};
|
|
|
|
|
|
|
|
// 获取数据库的摄像头信息
|
|
function getCameraData() {
|
|
return new Promise((resolve, reject) => {
|
|
fetch('/getSqlCamera', {
|
|
method: 'GET',
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
resolve(data);
|
|
})
|
|
.catch(error => {
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
// 根据摄像头信息显示摄像头的所有选项
|
|
getCameraData().then(data => {
|
|
const dropdown = document.getElementById('dropdown_cam');
|
|
dropdown.innerHTML = ''; // 清空现有选项
|
|
|
|
data.forEach(optionValue => {
|
|
const option = document.createElement('option');
|
|
option.value = optionValue[0];
|
|
option.text = optionValue[2];
|
|
dropdown.appendChild(option);
|
|
});
|
|
}).catch(error => {
|
|
console.error('获取摄像头数据出错:', error);
|
|
});
|
|
|
|
// 确定选择的摄像头,并传回后端
|
|
function submitCamera() {
|
|
var selectCamera = document.getElementById('dropdown_cam').value;
|
|
|
|
// 在这里执行您需要的操作,可以使用选定的选项进行相应的处理
|
|
var formData = new FormData();
|
|
formData.append('camID',selectCamera);
|
|
fetch('/getFrontCamera', {
|
|
method: 'POST',
|
|
body: formData
|
|
}).then(response => {
|
|
|
|
if (!response.ok) {
|
|
return response.text().then(text => { throw new Error(text || 'Response not OK') });
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
function showMessage(message, isError = false) {
|
|
var messageDiv = document.getElementById('message-container');
|
|
messageDiv.innerHTML = message;
|
|
messageDiv.className = isError ? 'status-message error' : 'status-message success';
|
|
|
|
// Hide the message after 5 seconds
|
|
setTimeout(() => {
|
|
messageDiv.innerHTML = '';
|
|
messageDiv.className = '';
|
|
}, 2000);
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|