secreen-capture-OCR / index.html
ginipick's picture
Update index.html
7318db9 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Screen Analysis & OCR Tool</title>
<!-- Include Tesseract.js for OCR -->
<script src="https://unpkg.com/tesseract.js@v2.1.0/dist/tesseract.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Arial, sans-serif;
}
body {
background: #f0f2f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.controls {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
display: flex;
align-items: center;
gap: 8px;
min-width: 120px;
justify-content: center;
}
.start-btn {
background: #4CAF50;
color: white;
}
.stop-btn {
background: #f44336;
color: white;
}
.clear-btn {
background: #2196F3;
color: white;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.preview-area {
background: black;
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
position: relative;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
#preview {
width: 100%;
aspect-ratio: 16/9;
object-fit: contain;
}
.recording-dot {
position: absolute;
top: 20px;
right: 20px;
width: 12px;
height: 12px;
background: red;
border-radius: 50%;
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.logs {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
.log-entry {
padding: 20px;
border-bottom: 1px solid #eee;
display: grid;
grid-template-columns: 300px 1fr;
gap: 20px;
}
.screenshot-container {
position: relative;
}
.screenshot {
width: 100%;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.change-highlight {
position: absolute;
border: 2px solid red;
background: rgba(255,0,0,0.2);
pointer-events: none;
}
.info-panel {
display: flex;
flex-direction: column;
gap: 15px;
}
.timestamp {
color: #666;
font-size: 14px;
}
.text-content {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
font-size: 14px;
line-height: 1.5;
max-height: 200px;
overflow-y: auto;
}
.actions {
display: flex;
gap: 10px;
margin-top: 10px;
}
.download-btn {
background: #4CAF50;
color: white;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 14px;
}
.processing {
color: #666;
font-style: italic;
}
@media (max-width: 768px) {
.log-entry {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Screen Analysis & OCR Tool</h1>
<p>Capture, analyze, and extract text from your screen</p>
</div>
<div class="controls">
<button id="startBtn" class="start-btn">▶ Start Capture</button>
<button id="stopBtn" class="stop-btn" disabled>⬛ Stop</button>
<button id="clearBtn" class="clear-btn">🗑 Clear Logs</button>
</div>
<div class="preview-area">
<video id="preview" autoplay></video>
<div class="recording-dot" style="display: none;"></div>
</div>
<div class="logs" id="logContainer"></div>
</div>
<script>
let mediaStream = null;
let captureInterval = null;
let lastImageData = null;
// Initialize Tesseract
const worker = Tesseract.createWorker();
(async () => {
await worker.load();
await worker.loadLanguage('eng');
await worker.initialize('eng');
})();
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const clearBtn = document.getElementById('clearBtn');
const preview = document.getElementById('preview');
const logContainer = document.getElementById('logContainer');
const recordingDot = document.querySelector('.recording-dot');
async function startCapture() {
try {
mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: { cursor: "always" }
});
preview.srcObject = mediaStream;
startBtn.disabled = true;
stopBtn.disabled = false;
recordingDot.style.display = 'block';
captureInterval = setInterval(captureAndAnalyze, 1000);
mediaStream.getVideoTracks()[0].onended = stopCapture;
} catch (err) {
console.error("Error starting capture:", err);
}
}
function stopCapture() {
if (mediaStream) {
mediaStream.getTracks().forEach(track => track.stop());
preview.srcObject = null;
}
clearInterval(captureInterval);
startBtn.disabled = false;
stopBtn.disabled = true;
recordingDot.style.display = 'none';
lastImageData = null;
}
function detectChanges(current, last) {
const blockSize = 20;
const threshold = 30;
const changes = [];
const width = current.width;
const height = current.height;
for (let y = 0; y < height; y += blockSize) {
for (let x = 0; x < width; x += blockSize) {
let diffCount = 0;
const maxY = Math.min(y + blockSize, height);
const maxX = Math.min(x + blockSize, width);
for (let py = y; py < maxY; py++) {
for (let px = x; px < maxX; px++) {
const i = (py * width + px) * 4;
if (Math.abs(current.data[i] - last.data[i]) > threshold ||
Math.abs(current.data[i + 1] - last.data[i + 1]) > threshold ||
Math.abs(current.data[i + 2] - last.data[i + 2]) > threshold) {
diffCount++;
}
}
}
if (diffCount > (blockSize * blockSize * 0.3)) {
changes.push({
x: x / width * 100,
y: y / height * 100,
width: Math.min(blockSize / width * 100, 100 - x / width * 100),
height: Math.min(blockSize / height * 100, 100 - y / height * 100)
});
}
}
}
return changes;
}
async function captureAndAnalyze() {
const canvas = document.createElement('canvas');
canvas.width = preview.videoWidth;
canvas.height = preview.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(preview, 0, 0);
const currentImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let changes = [];
if (lastImageData) {
changes = detectChanges(currentImageData, lastImageData);
}
lastImageData = currentImageData;
if (changes.length > 0 || !lastImageData) {
const imageUrl = canvas.toDataURL('image/jpeg', 0.8);
await addLogEntry(imageUrl, changes);
}
}
async function performOCR(imageUrl) {
try {
const result = await worker.recognize(imageUrl);
return result.data.text;
} catch (error) {
console.error('OCR Error:', error);
return 'Error performing OCR';
}
}
async function addLogEntry(imageUrl, changes) {
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
const timestamp = new Date().toLocaleString();
// Create initial structure with loading state
logEntry.innerHTML = `
<div class="screenshot-container">
<img class="screenshot" src="${imageUrl}" alt="Screenshot">
${changes.map(change => `
<div class="change-highlight" style="
left: ${change.x}%;
top: ${change.y}%;
width: ${change.width}%;
height: ${change.height}%;
"></div>
`).join('')}
</div>
<div class="info-panel">
<div class="timestamp">${timestamp}</div>
<div class="text-content processing">Processing text extraction...</div>
<div class="actions">
<a href="${imageUrl}" download="screenshot-${Date.now()}.jpg" class="download-btn">
💾 Download Image
</a>
</div>
</div>
`;
logContainer.insertBefore(logEntry, logContainer.firstChild);
// Perform OCR
const extractedText = await performOCR(imageUrl);
// Update text content
const textContent = logEntry.querySelector('.text-content');
textContent.classList.remove('processing');
textContent.textContent = extractedText || 'No text detected';
}
clearBtn.addEventListener('click', () => {
logContainer.innerHTML = '';
});
startBtn.addEventListener('click', startCapture);
stopBtn.addEventListener('click', stopCapture);
// Cleanup when page is closed
window.addEventListener('beforeunload', () => {
worker.terminate();
});
</script>
</body>
</html>