bightbranding's picture
undefined - Initial Deployment
ffeb37d verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Auto Photogrammetry Camera Tracker - نسخه وب</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Vazirmatic:wght@300;400;500;700&display=swap');
body {
font-family: 'Vazirmatic', sans-serif;
}
.perspective-1000 {
perspective: 1000px;
}
.transform-style-3d {
transform-style: preserve-3d;
}
.rotate-y-15 {
transform: rotateY(15deg);
}
.glass-effect {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.progress-ring {
transform: rotate(-90deg);
}
.progress-ring__circle {
transition: stroke-dashoffset 0.35s;
transform-origin: 50% 50%;
}
.point-cloud {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.floating-ui {
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
}
.glow-effect {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.5);
}
.scan-line {
background: linear-gradient(90deg,
transparent 0%,
rgba(59, 130, 246, 0.8) 50%,
transparent 100%);
animation: scan 2s linear infinite;
}
@keyframes scan {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen overflow-x-hidden">
<!-- Background Animation -->
<div id="background-animation" class="fixed inset-0 z-0"></div>
<div class="relative z-10">
<!-- Header -->
<header class="glass-effect border-b border-gray-700">
<div class="container mx-auto px-4 py-6">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<div class="w-12 h-12 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-xl">3D</span>
</div>
<div>
<h1 class="text-2xl font-bold">Auto Photogrammetry Camera Tracker</h1>
<p class="text-gray-400 text-sm">تبدیل ویدیو به محیط سه‌بعدی با هوش مصنوعی</p>
</div>
</div>
<div class="flex items-center space-x-4">
<button id="settingsBtn" class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-colors">
<span class="material-icons">settings</span>
</button>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<div class="grid lg:grid-cols-3 gap-8">
<!-- Control Panel -->
<div class="lg:col-span-1 space-y-6">
<!-- Upload Section -->
<div class="glass-effect rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<span class="material-icons mr-2">upload</span>
بارگذاری ویدیو
</h3>
<div id="uploadArea" class="border-2 border-dashed border-gray-600 rounded-lg p-8 text-center hover:border-blue-500 transition-colors cursor-pointer">
<span class="material-icons text-6xl text-gray-400 mb-4">video_library</span>
<p class="text-gray-300">ویدیو خود را اینجا رها کنید یا کلیک کنید</p>
<input type="file" id="videoInput" accept="video/*" class="hidden">
</div>
<div id="videoInfo" class="hidden mt-4 p-4 bg-gray-800 rounded-lg">
<p class="text-sm text-gray-300">
<strong>نام فایل:</strong> <span id="fileName">-</span><br>
<strong>اندازه:</strong> <span id="fileSize">-</span><br>
<strong>مدت زمان:</strong> <span id="duration">-</span>
</p>
</div>
</div>
<!-- Settings Panel -->
<div class="glass-effect rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<span class="material-icons mr-2">tune</span>
تنظیمات پردازش
</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium mb-2">نرخ نمونه‌برداری فریم</label>
<input type="range" id="frameRate" min="1" max="30" value="5" class="w-full">
<span class="text-sm text-gray-400">هر <span id="frameRateValue">5</span> فریم</span>
</div>
<div>
<label class="block text-sm font-medium mb-2">آستانه تطابق</label>
<input type="range" id="matchThreshold" min="0.1" max="1" step="0.1" value="0.7" class="w-full">
<span class="text-sm text-gray-400"><span id="thresholdValue">0.7</span></span>
</div>
<div>
<label class="block text-sm font-medium mb-2">طول کانونی (mm)</label>
<input type="number" id="focalLength" value="35" class="w-full bg-gray-800 rounded px-3 py-2">
</div>
<div class="flex items-center">
<input type="checkbox" id="lensCorrection" class="mr-2">
<label class="text-sm">اصلاح اعوجاج لنز</label>
</div>
</div>
</div>
<!-- Process Button -->
<button id="processBtn" disabled class="w-full bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 disabled:from-gray-600 disabled:to-gray-700 disabled:cursor-not-allowed rounded-xl p-4 font-semibold transition-all glow-effect">
<span class="material-icons mr-2">play_circle</span>
شروع پردازش
</button>
<!-- Progress Section -->
<div id="progressSection" class="glass-effect rounded-xl p-6 hidden">
<h3 class="text-lg font-semibold mb-4">پردازش...</h3>
<div class="space-y-4">
<div>
<div class="flex justify-between text-sm mb-1">
<span>استخراج فریم‌ها</span>
<span id="progress1">0%</span>
</div>
<div class="w-full bg-gray-700 rounded-full h-2">
<div id="bar1" class="bg-blue-500 h-2 rounded-full" style="width: 0%"></div>
</div>
</div>
<div>
<div class="flex justify-between text-sm mb-1">
<span>تشخیص ویژگی‌ها</span>
<span id="progress2">0%</span>
</div>
<div class="w-full bg-gray-700 rounded-full h-2">
<div id="bar2" class="bg-purple-500 h-2 rounded-full" style="width: 0%"></div>
</div>
</div>
<div>
<div class="flex justify-between text-sm mb-1">
<span>ساخت نقطه‌ابر</span>
<span id="progress3">0%</span>
</div>
<div class="w-full bg-gray-700 rounded-full h-2">
<div id="bar3" class="bg-green-500 h-2 rounded-full" style="width: 0%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 3D Viewer -->
<div class="lg:col-span-2">
<div class="glass-effect rounded-xl p-6 h-full">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold flex items-center">
<span class="material-icons mr-2">view_in_ar</span>
پیش‌نمایش سه‌بعدی
</h3>
<div class="flex space-x-2">
<button id="resetView" class="p-2 bg-gray-800 rounded hover:bg-gray-700">
<span class="material-icons text-sm">refresh</span>
</button>
<button id="toggleWireframe" class="p-2 bg-gray-800 rounded hover:bg-gray-700">
<span class="material-icons text-sm">grid_3x3</span>
</button>
</div>
</div>
<div id="viewer3d" class="relative bg-gray-800 rounded-lg h-96 lg:h-[500px]">
<div id="threejs-container" class="w-full h-full rounded-lg overflow-hidden"></div>
<!-- Point Cloud Overlay -->
<div id="pointCloudOverlay" class="point-cloud hidden">
<canvas id="pointCloudCanvas" class="w-full h-full"></canvas>
</div>
<!-- Camera Path Indicator -->
<div id="cameraPath" class="absolute inset-0 hidden">
<svg class="w-full h-full" viewBox="0 0 400 300">
<path id="cameraPathSvg" stroke="#3B82F6" stroke-width="2" fill="none" opacity="0.7"/>
<circle id="cameraPos" r="4" fill="#3B82F6" class="glow-effect"/>
</svg>
</div>
</div>
<!-- Stats Panel -->
<div class="mt-4 grid grid-cols-2 lg:grid-cols-4 gap-4">
<div class="glass-effect rounded-lg p-4 text-center">
<span class="material-icons text-blue-500 mb-2">photo_camera</span>
<p class="text-sm text-gray-400">فریم‌های پردازش شده</p>
<p id="framesProcessed" class="text-xl font-bold">0</p>
</div>
<div class="glass-effect rounded-lg p-4 text-center">
<span class="material-icons text-purple-500 mb-2">grain</span>
<p class="text-sm text-gray-400">نقاط سه‌بعدی</p>
<p id="pointsCount" class="text-xl font-bold">0</p>
</div>
<div class="glass-effect rounded-lg p-4 text-center">
<span class="material-icons text-green-500 mb-2">timeline</span>
<p class="text-sm text-gray-400">ترکینگ دوربین</p>
<p id="trackingStatus" class="text-sm">غیرفعال</p>
</div>
<div class="glass-effect rounded-lg p-4 text-center">
<span class="material-icons text-orange-500 mb-2">memory</span>
<p class="text-sm text-gray-400">زمان پردازش</p>
<p id="processingTime" class="text-xl font-bold">0s</p>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Results Modal -->
<div id="resultsModal" class="fixed inset-0 bg-black bg-opacity-75 z-50 hidden flex items-center justify-center">
<div class="glass-effect rounded-xl p-8 max-w-2xl w-full mx-4">
<div class="text-center">
<div class="w-20 h-20 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4">
<span class="material-icons text-4xl">check_circle</span>
</div>
<h2 class="text-2xl font-bold mb-4">پردازش با موفقیت انجام شد!</h2>
<p class="text-gray-300 mb-6">محیط سه‌بعدی از ویدیوی شما با موفقیت ساخته شد</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="glass-effect rounded-lg p-4">
<p class="text-2xl font-bold text-blue-400" id="finalFrames">0</p>
<p class="text-sm text-gray-400">فریم پردازش شده</p>
</div>
<div class="glass-effect rounded-lg p-4">
<p class="text-2xl font-bold text-purple-400" id="finalPoints">0</p>
<p class="text-sm text-gray-400">نقطه سه‌بعدی</p>
</div>
<div class="glass-effect rounded-lg p-4">
<p class="text-2xl font-bold text-green-400" id="finalTime">0s</p>
<p class="text-sm text-gray-400">زمان کل</p>
</div>
</div>
<div class="flex justify-center space-x-4">
<button id="downloadBtn" class="bg-blue-600 hover:bg-blue-700 px-6 py-2 rounded-lg transition-colors">
<span class="material-icons mr-2">download</span>
دانلود نتایج
</button>
<button id="closeModal" class="bg-gray-700 hover:bg-gray-600 px-6 py-2 rounded-lg transition-colors">
بستن
</button>
</div>
</div>
</div>
</div>
</div>
<script>
// Global variables
let scene, camera, renderer, pointCloud;
let animationId;
let processingStartTime;
// Initialize Three.js scene
function init3DViewer() {
const container = document.getElementById('threejs-container');
// Scene setup
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a1a);
// Camera setup
camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
// Renderer setup
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(renderer.domElement);
// Add grid
const gridHelper = new THREE.GridHelper(10, 10, 0x444444, 0x444444);
scene.add(gridHelper);
// Add axes helper
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// Add lights
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// Create initial point cloud
createPointCloud();
// Animation loop
animate();
}
function createPointCloud() {
const geometry = new THREE.BufferGeometry();
const positions = [];
const colors = [];
// Generate random points
for (let i = 0; i < 1000; i++) {
positions.push(
(Math.random() - 0.5) * 10,
Math.random() * 5,
(Math.random() - 0.5) * 10
);
const color = new THREE.Color();
color.setHSL(Math.random(), 0.7, 0.5);
colors.push(color.r, color.g, color.b);
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
const material = new THREE.PointsMaterial({
size: 0.05,
vertexColors: true,
opacity: 0.8,
transparent: true
});
pointCloud = new THREE.Points(geometry, material);
scene.add(pointCloud);
}
function animate() {
animationId = requestAnimationFrame(animate);
if (pointCloud) {
pointCloud.rotation.y += 0.001;
}
renderer.render(scene, camera);
}
// Handle window resize
window.addEventListener('resize', () => {
if (renderer && camera) {
const container = document.getElementById('threejs-container');
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}
});
// UI interactions
document.addEventListener('DOMContentLoaded', () => {
init3DViewer();
// File upload handling
const uploadArea = document.getElementById('uploadArea');
const videoInput = document.getElementById('videoInput');
const processBtn = document.getElementById('processBtn');
uploadArea.addEventListener('click', () => videoInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('border-blue-500');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('border-blue-500');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('border-blue-500');
handleFile(e.dataTransfer.files[0]);
});
videoInput.addEventListener('change', (e) => {
handleFile(e.target.files[0]);
});
function handleFile(file) {
if (file && file.type.startsWith('video/')) {
const video = document.createElement('video');
video.src = URL.createObjectURL(file);
video.addEventListener('loadedmetadata', () => {
document.getElementById('fileName').textContent = file.name;
document.getElementById('fileSize').textContent = (file.size / 1024 / 1024).toFixed(2) + ' MB';
document.getElementById('duration').textContent = Math.floor(video.duration) + ' ثانیه';
document.getElementById('videoInfo').classList.remove('hidden');
processBtn.disabled = false;
});
}
}
// Settings sliders
document.getElementById('frameRate').addEventListener('input', (e) => {
document.getElementById('frameRateValue').textContent = e.target.value;
});
document.getElementById('matchThreshold').addEventListener('input', (e) => {
document.getElementById('thresholdValue').textContent = e.target.value;
});
// Process button
processBtn.addEventListener('click', startProcessing);
// Modal controls
document.getElementById('closeModal').addEventListener('click', () => {
document.getElementById('resultsModal').classList.add('hidden');
});
document.getElementById('downloadBtn').addEventListener('click', () => {
alert('در نسخه واقعی: دانلود فایل‌های خروجی Blender');
});
});
// Processing simulation
function startProcessing() {
processingStartTime = Date.now();
document.getElementById('progressSection').classList.remove('hidden');
document.getElementById('processBtn').disabled = true;
const steps = [
{ id: 'bar1', progress: 'progress1', duration: 2000 },
{ id: 'bar2', progress: 'progress2', duration: 3000 },
{ id: 'bar3', progress: 'progress3', duration: 2500 }
];
let completedSteps = 0;
steps.forEach((step, index) => {
let progress = 0;
const interval = setInterval(() => {
progress += 1;
const percent = Math.min(progress, 100);
document.getElementById(step.id).style.width = percent + '%';
document.getElementById(step.progress).textContent = percent + '%';
// Update stats
if (index === 0) {
document.getElementById('framesProcessed').textContent = Math.floor(percent * 2.4);
} else if (index === 1) {
document.getElementById('pointsCount').textContent = Math.floor(percent * 30);
}
if (progress >= 100) {
clearInterval(interval);
completedSteps++;
if (completedSteps === steps.length) {
finishProcessing();
}
}
}, step.duration / 100);
});
}
function finishProcessing() {
const totalTime = Math.floor((Date.now() - processingStartTime) / 1000);
document.getElementById('processingTime').textContent = totalTime + 's';
document.getElementById('trackingStatus').textContent = 'فعال';
// Update final stats
document.getElementById('finalFrames').textContent = document.getElementById('framesProcessed').textContent;
document.getElementById('finalPoints').textContent = document.getElementById('pointsCount').textContent;
document.getElementById('finalTime').textContent = totalTime + 's';
// Show camera path
document.getElementById('cameraPath').classList.remove('hidden');
animateCameraPath();
// Show results modal after delay
setTimeout(() => {
document.getElementById('resultsModal').classList.remove('hidden');
}, 1000);
}
function animateCameraPath() {
const path = document.getElementById('cameraPathSvg');
const pos = document.getElementById('cameraPos');
// Create a simple camera path
let d = 'M 50 250';
for (let i = 0; i < 300; i += 10) {
const x = 50 + i;
const y = 250 + Math.sin(i * 0.02) * 50;
d += ` L ${x} ${y}`;
}
path.setAttribute('d', d);
// Animate camera position
let progress = 0;
const animateCamera = () => {
progress += 0.01;
if (progress > 1) progress = 0;
const point = path.getPointAtLength(progress * path.getTotalLength());
pos.setAttribute('cx', point.x);
pos.setAttribute('cy', point.y);
requestAnimationFrame(animateCamera);
};
animateCamera();
}
// Background animation
function createBackgroundAnimation() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const container = document.getElementById('background-animation');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
container.appendChild(canvas);
const particles = [];
const particleCount = 100;
for (let i = 0; i < particleCount; i++) {
particles.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
size: Math.random() * 2 + 1,
opacity: Math.random() * 0.5 + 0.1
});
}
function animate() {
ctx.fillStyle = 'rgba(26, 26, 26, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
particles.forEach(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1;
if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1;
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(59, 130, 246, ${particle.opacity})`;
ctx.fill();
});
requestAnimationFrame(animate);
}
animate();
}
createBackgroundAnimation();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=bightbranding/auto-photogrammetry-camera-tracker" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>