Add soble edge detection

This commit is contained in:
Harshit-Dhanwalkar 2026-05-20 17:54:48 +05:30
parent c1f0298dae
commit 9eaf33ea36
4 changed files with 71 additions and 14 deletions

View file

@ -1,5 +1,6 @@
#include "ascii.h"
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@ -50,6 +51,31 @@ size_t ascii_out_size(int dst_w, int dst_h, int color) {
}
}
// Sobel edge detection (kernel convolution)
static void sobel(const uint8_t *in, uint8_t *out, int w, int h) {
static const int Gx[3][3] = {{-1, 0, 1}, {-2, 0, -2}, {-1, 0, 1}};
static const int Gy[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
for (int y = 1; y < h - 1; y++) {
for (int x = 1; x < w - 1; x++) {
int gx = 0, gy = 0;
for (int ky = -1; ky <= 1; ky++) {
for (int kx = -1; kx <= 1; kx++) {
int p = in[(y + ky) * w + (x + kx)];
gx += Gx[ky + 1][kx + 1] * p;
gy += Gy[ky + 1][kx + 1] * p;
}
}
// TEST: test both L1 and L2 normalisations
int mag = abs(gx) + abs(gy); // L1 normalisation
// int mag = sqrt(gx * gx + gy * gy); // L2 normalisation
out[y * w + x] = (uint8_t)(mag > 255 ? 255 : mag);
}
}
}
// Grayscale to ascii
int grayscale_to_ascii(const uint8_t *gray, const uint8_t *rgb, int src_w,
int src_h, int dst_w, int dst_h, char *out,
@ -61,6 +87,7 @@ int grayscale_to_ascii(const uint8_t *gray, const uint8_t *rgb, int src_w,
int contrast = opts ? opts->contrast : 100;
int invert = opts ? opts->invert : 0;
int do_color = opts && opts->color && (rgb != NULL);
int do_edges = opts ? opts->edges : 0;
int do_dither = opts ? opts->dither : 0;
// Blocking Widht and height pixels in source pixels
@ -123,6 +150,18 @@ int grayscale_to_ascii(const uint8_t *gray, const uint8_t *rgb, int src_w,
}
}
// Sobel edge detection
if (do_edges) {
// Temporary buffer for detected edges results
uint8_t *edge_buf = calloc(dst_w * dst_h, sizeof(uint8_t));
if (edge_buf) {
sobel(small_g, edge_buf, dst_w, dst_h);
// overwite grayscale image with edge map
memcpy(small_g, edge_buf, dst_w * dst_h);
free(edge_buf);
}
}
// Floyd-Steinberg dithering
if (do_dither) {
int16_t *eb = malloc(dst_w * dst_h * sizeof(int16_t));

View file

@ -1,18 +1,18 @@
#include "ascii.h"
#include "capture.h"
#include "timing.h"
#include "thread_sharing.h"
#include "timing.h"
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>
// Defaults
#define DEFAULT_ASCII_WIDTH 80
@ -51,6 +51,7 @@ static void print_usage(const char *prog) {
" -b <val> brightness offset -128..128 (default: 0)\n"
" -c <val> contrast in percent >0; 100=none (default: 100)\n"
" -i invert brightness->charset mapping\n"
" -e enable Sobel edge detection\n"
" -C colour output (ANSI truecolor)\n"
" -D Floyd-Steinberg dithering\n",
prog, DEFAULT_CAPTURE_WIDTH, DEFAULT_CAPTURE_HEIGHT, DEFAULT_FPS,
@ -62,7 +63,7 @@ void term_raw_mode(void) {
tcgetattr(STDOUT_FILENO, &orig_terminal);
struct termios raw = orig_terminal;
raw.c_lflag &= ~(ICANON | ECHO); // no line buffering or no echo
raw.c_cc[VMIN] = 0; // non-blocking read
raw.c_cc[VMIN] = 0; // non-blocking read
raw.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
@ -87,13 +88,14 @@ int main(int argc, char *argv[]) {
.contrast = 100,
.invert = 0,
.color = 0,
.edges = 0,
.dither = 0,
.charset = NULL,
};
// CLI parsing
int opt;
while ((opt = getopt(argc, argv, "d:W:H:w:h:f:b:c:iCDs:")) != -1) {
while ((opt = getopt(argc, argv, "ed:W:H:w:h:f:b:c:iCDs:")) != -1) {
switch (opt) {
case 'd':
device = optarg;
@ -137,6 +139,9 @@ int main(int argc, char *argv[]) {
case 'C':
opts.color = 1;
break;
case 'e':
opts.edges = 1;
break;
case 'D':
opts.dither = 1;
break;
@ -159,8 +164,8 @@ int main(int argc, char *argv[]) {
}
fprintf(stderr, "Device: %s | capture %dx%d | ASCII %dx%d | %d fps%s%s%s\n",
device, cam.width, cam.height, ascii_w, ascii_h, fps,
opts.color ? " | color" : "", opts.dither ? " | dither" : "",
opts.invert ? " | inverted" : "");
opts.color ? " | color" : "", opts.edges ? " | edges" : "",
opts.dither ? " | dither" : "", opts.invert ? " | inverted" : "");
// Allocate pixel buffers
int cam_pixels = cam.width * cam.height;