Skip to content

Commit

Permalink
improved line following
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Lei authored and Brandon Lei committed Oct 30, 2024
1 parent 59e2f0c commit bc5adef
Showing 1 changed file with 46 additions and 114 deletions.
160 changes: 46 additions & 114 deletions LineDetection.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Black Line Detection</title>
<title>Line Detection</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; }
#outputImage { max-width: 100%; height: auto; border: 1px solid #ddd; }
Expand All @@ -25,88 +25,35 @@
this.canvas.height = height;
this.ctx = this.canvas.getContext('2d');
this.lastDetectedLine = [];
this.isProcessing = false;
this.frameCount = 0;
this.allCoordinates = [];
}

async detectLine() {
if (this.isProcessing) return this.canvas.toDataURL();
this.isProcessing = true;

try {
const image = await this.loadImage(`http://${this.raspberryPiIp}:${port.camera}/${endpoint.video}`);
this.ctx.drawImage(image, 0, 0, this.width, this.height);
const imageData = this.ctx.getImageData(0, 0, this.width, this.height);

// Convert to grayscale with extreme contrast to isolate dark line
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const gray = (data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114);
// Extreme contrast to isolate dark line
const contrast = 5.0;
const brightnessFactor = -50;
const enhancedGray = Math.min(255, Math.max(0, (gray - 128) * contrast + 128 + brightnessFactor));
// Very aggressive threshold - only keep the darkest pixels
const threshold = 60;
const binaryValue = enhancedGray < threshold ? 0 : 255;
data[i] = binaryValue;
data[i + 1] = binaryValue;
data[i + 2] = binaryValue;
data[i + 3] = 255;
const lineCoordinates = this.processImageData(imageData);
console.log("coordinates");
console.log(lineCoordinates);
if (lineCoordinates.length > 0) {
this.lastDetectedLine = lineCoordinates;
}

// Find the single longest continuous dark line
const contours = this.findContours(imageData);
let longestLine = null;
let maxLength = 0;

for (const contour of contours) {
if (contour.length > maxLength) {
// Verify it's a line-like shape
const xs = contour.map(p => p[0]);
const ys = contour.map(p => p[1]);
const width = Math.max(...xs) - Math.min(...xs);
const height = Math.max(...ys) - Math.min(...ys);
const ratio = Math.max(width, height) / Math.min(width, height);

// Must be elongated and continuous
if (ratio > 3 && contour.length > 50) {
maxLength = contour.length;
longestLine = contour;
}
if (this.frameCount < 7) {
this.allCoordinates.push(lineCoordinates);
this.frameCount++;
if (this.frameCount === 7) {
this.writeCoordinatesToFile(this.allCoordinates);
}
}

// Clear and draw original image faintly
this.ctx.clearRect(0, 0, this.width, this.height);
this.ctx.globalAlpha = 0.3;
this.ctx.drawImage(image, 0, 0, this.width, this.height);
this.ctx.globalAlpha = 1.0;

// Draw detected line in blue
if (longestLine) {
this.ctx.beginPath();
this.ctx.strokeStyle = '#0000FF';
this.ctx.lineWidth = 3;
this.ctx.lineCap = 'round';
this.ctx.lineJoin = 'round';

longestLine.forEach(([x, y], i) => {
if (i === 0) {
this.ctx.moveTo(x, y);
} else {
this.ctx.lineTo(x, y);
}
});
this.ctx.stroke();
this.lastDetectedLine = longestLine;
}

this.drawLine(this.lastDetectedLine);
return this.canvas.toDataURL();
} catch (error) {
console.error('Error detecting line:', error);
return null;
} finally {
this.isProcessing = false;
}
}

Expand All @@ -120,58 +67,43 @@
});
}

findContours(imageData) {
const contours = [];
const data = imageData.data;
const visited = new Set();

processImageData(imageData) {
const lineCoordinates = [];
const threshold = 70;

for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
const idx = (y * this.width + x) * 4;
const key = `${x},${y}`;
const index = (y * this.width + x) * 4;
const [r, g, b] = imageData.data.slice(index, index + 3);

if (data[idx] === 0 && !visited.has(key)) {
const contour = [];
this.traceContour(x, y, data, visited, contour);
if (contour.length > 0) {
contours.push(contour);
}
}
if (r < threshold && g < threshold && b < threshold && y < 400) {
lineCoordinates.push([x, y]);
}
}
}
return contours;
return lineCoordinates.sort((a, b) => a[1] - b[1]);
}

traceContour(startX, startY, data, visited, contour) {
const stack = [[startX, startY]];

while (stack.length > 0) {
const [x, y] = stack.pop();
const key = `${x},${y}`;

if (visited.has(key)) continue;

visited.add(key);
contour.push([x, y]);

// Check 8-connected neighbors
const neighbors = [
[1, 0], [1, 1], [0, 1], [-1, 1],
[-1, 0], [-1, -1], [0, -1], [1, -1]
];

for (const [dx, dy] of neighbors) {
const nx = x + dx;
const ny = y + dy;

if (nx >= 0 && nx < this.width && ny >= 0 && ny < this.height) {
const idx = (ny * this.width + nx) * 4;
if (data[idx] === 0 && !visited.has(`${nx},${ny}`)) {
stack.push([nx, ny]);
}
}
}
}
drawLine(coordinates) {
this.ctx.beginPath();
this.ctx.strokeStyle = 'blue';
this.ctx.lineWidth = 2;
coordinates.forEach(([x, y], i) => {
this.ctx.arc(x, y, 5, 0, Math.PI*2);
});
this.ctx.stroke();
}

writeCoordinatesToFile(allCoordinates) {
const content = allCoordinates.map((frame, index) =>
`Frame ${index + 1}:\n${frame.map(coord => coord.join(',')).join('\n')}\n`
).join('\n');
const blob = new Blob([content], { type: 'text/plain' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'coordinates.txt';
a.click();
URL.revokeObjectURL(a.href);
}
}

Expand Down Expand Up @@ -201,11 +133,11 @@
</script>
</head>
<body>
<h1>Black Line Detection</h1>
<h1>Line Detection</h1>
<div id="controls">
<input type="text" id="ipInput" placeholder="IP Address">
<button onclick="initializeDetector()">Start Detection</button>
</div>
<img id="outputImage" alt="Processed Image">
</body>
</html>
</html>

0 comments on commit bc5adef

Please sign in to comment.