Los Caballeros

"Sobre seguridad, programación, modding, frikismo, etc... "

Logic-Key Regresa!

Twitter icon Twitter icon
Así como dice el titulo, estamos pensando volver a la vida a Logic-Key (para los que nunca la leyeron es una e-zine del 2007 de la cual solo se publico una edición xD).

Necesitamos:
  • Redactores.
  • Correctores.
  • Diseñadores gráficos.
cualquier interesado: xianur0.null [at] gmail [dot] com

Saludos!

Detección de presencia con OpenCV

Twitter icon Twitter icon

Aquí Xianur0. Este es un algoritmo simple, se genera una imagen del fondo y se comparan los pixeles "agregados" de modo que si algo no coincide entonces tenemos que ese algo no estaba ahí, por tanto reconocemos presencias.
Aparte se incluye un algoritmo de corrección de falsos positivos (elimina manchas pequeñas) de este modo es ideal para utilizar con cámaras de baja calidad (por ejemplo en la imagen estoy utilizando una de 1.3 Mpx xD), del mismo modo ese mismo algoritmo se podría utilizar para corregir defectos de cámaras (mejorar imágenes) pero eso sera para otro día :P

#include <opencv2/video/background_segm.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <stdio.h>
// By Xianur0
// http://hackingtelevision.blogspot.com/

using namespace cv;

void mensaje()
{
printf("./presencia [camara]\n\n");
}

int main(int argc, char** argv)
{
VideoCapture cap;
bool updatebg = true;
if( argc < 2 )
cap.open(0);
else
cap.open(atoi(argv[1]));
mensaje();
if( !cap.isOpened() )
{
printf("No se puede abrir :S\n");
return -1;
}
BackgroundSubtractorMOG2 bg_model;
Mat frame, variaciones;
for(;;)
{
cap >> frame;
if( frame.empty() )
break;
bg_model(frame, variaciones, updatebg ? -1 : 0);
updatebg = false;
IplImage *in = new IplImage(variaciones);
IplImage *in2 = new IplImage(frame);
char *data = in->imageData;
int x,y,i = 0;
int inicio[in->width*in->height][4];
int contador = 0;
// limpiesa previa
inicio[0][0] = 0;
for(y=0;y<in->height;y++) {
for(x=0;x<in->width;x++) {
if(data[y*in->widthStep+x*in->nChannels] != 0) {
if(inicio[contador][0] == 0) {
inicio[contador][0] = x;
inicio[contador][1] = y;
}
} else {
if(inicio[contador][0] != 0) {
inicio[contador][2] = x;
inicio[contador][3] = y;
contador++;
inicio[contador][0] = 0;
}
}
}
}
printf("Limpiando: %i manchas\n",contador);
int manchas = 0;
for(i = 0;i<contador;i++) {
if(inicio[i][1] == inicio[i][3] && (inicio[i][2]-inicio[i][0]) < 5) {
for(x=inicio[i][0]-1;x<inicio[i][2];x++) {
data[(inicio[i][1])*in->widthStep+x*in->nChannels] = 0;
}
} else {
manchas++;
}
}
printf("Se ignoraron %i manchas\n",manchas);
// ultimo bucle para localizar puntos (confiando en que el filtro anterior lo dejo muy "bonito")
int masalto = 0;
int masbajo = 0;
int masizquierda = 0;
int masderecha = 0;
for(x=1;x<in->width;x++) {
for(y=0;y<in->height;y++) {
if(data[y*in->widthStep+x*in->nChannels] != 0) {
if(x > masderecha) {
masderecha = x;
}
if(x < masizquierda) {
masizquierda = x;
}
if(y > masalto) {
masalto = y;
}
if(y < masbajo) {
masbajo = y;
}
}
}
}
int centrox = (masderecha+masizquierda)/2;
int centroy = (masalto+masbajo)/2;
int radius = abs(masalto-centroy);
cvCircle(in2, cvPoint(centrox,centroy), radius, cvScalar(0,255,0), 1);
imshow("RGB", in2);
imshow("Presencia", in);
char k = (char)waitKey(30);
if( k == 27 ) break;
if( k == ' ' )
{
updatebg = !updatebg;
if(updatebg)
printf("Background update activado\n");
else
printf("Background update desactivado\n");
}
}
return 0;
}

OCR con OpenCV usando Canny

Twitter icon Twitter icon
Xianur0 reportándose (aja, sigo vivo xD)

Imagen:

Resultado:




Este es un algoritmo mas de OCR que me arme usando OpenCV y el algoritmo Canny (y algunas otras ideas de mi invención).

Como funciona?

Primero se realiza la segmentación con ayuda de canny (bordes encontrados), es decir mediante canny se elimina basura de la imagen y ademas nos permite separar cosas del fondo, en este caso texto, ya que se tienen las letras (o caracteres) en versión de bordes únicamente tenemos que realizar un barrido horizontal y vertical para separar las letras, luego en caso de ser necesario un doble barrido por sector separado, de este modo tendremos las letras específicamente separadas del fondo, las cuales podemos fácilmente copiar y pegar e indicar que es lo que tiene cada cuadro, de este modo mediante comparación de pixeles podemos hacer que el sistema deduzca que es lo que hay en el cuadro... como verán el detalle seria el tamaño, pero únicamente hay que agregar una función para re-dimensionar y poner un tamaño estándar y tendremos un OCR bastante efectivo y fácil de "entrenar". Saludos!

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <stdio.h>
#include <iostream>

// By Xianur0
// http://hackingtelevision.blogspot.com/

using namespace cv;
using namespace std;

void help()
{
cout <<
"\nSegmentacion usando canny\n"
"./espacips [imagen]\n" << endl;

}

int edgeThresh = 100;
Mat image, gray, edge, cedge;

IplImage * cortar(IplImage *in, int x, int y, int w, int h) {
cvSetImageROI(in, cvRect(x, y, w, h));
IplImage *img = cvCreateImage(cvGetSize(in),
in->depth,
in->nChannels);
cvCopy(in, img, NULL);
cvResetImageROI(in);
return img;
}

void procesarcaracteres(IplImage *in, int id) {
int x,y = 0;
IplImage *in2 = cvCreateImage(cvGetSize(in),
in->depth,
in->nChannels);
cvCopy(in,in2, NULL);
char *data = in->imageData;
char *data2 = in2->imageData;
int tmp[2];
tmp[0] = 0;
tmp[1] = 0;
int tlineasv = 0;
int anteriorx=0;
int anteriory=0;
int lineaanterior = 0;
int lineas[in->height];
for(x=0;x<in->width;x++) {
for(y=0;y<in->height;y++) {
if(
data[y*in->widthStep+x*in->nChannels] != 0 ||
data[y*in->widthStep+x*in->nChannels+1] != 0 ||
data[y*in->widthStep+x*in->nChannels+2] != 0
) {
if(tmp[0] != x) {
tmp[0] = x;
tmp[1] = y;
break;
}
}
}
if(abs(tmp[0]-anteriorx) > 1) {
if((abs(tmp[1]-anteriory) < 2 || abs(tmp[0]-anteriorx) > 3 || abs(tmp[1]-anteriory) > 3) && (lineaanterior == 0 || abs(lineaanterior-tmp[0]) > 10)) {
// printf("Trazando linea verticales: (X: %i)\n",x);
lineas[tlineasv] = x;
for(y=(in->height-1);y>=0;y--) {
data2[y*in->widthStep+x*in->nChannels] = 0;
data2[y*in->widthStep+x*in->nChannels+1] = 0;
data2[y*in->widthStep+x*in->nChannels+2] = 255;
}
tlineasv++;
}
lineaanterior = tmp[0];
}
anteriorx = tmp[0];
anteriory = tmp[1];
}
x = tmp[0];
lineas[tlineasv] = x;
for(y=(in->height-1);y>=0;y--) {
data2[y*in->widthStep+x*in->nChannels] = 255;
data2[y*in->widthStep+x*in->nChannels+1] = 255;
data2[y*in->widthStep+x*in->nChannels+2] = 255;
}
tlineasv++;
// printf("Comenzando a recortar...\n");
if(tlineasv >=2)
for(x = 0; x < tlineasv-1; x++){
if(x+1 >= tlineasv) lineas[x+1] = in->width;
char buff[(sizeof(int)*2)+3];
sprintf(buff,"c%i: %i",id,x);
imshow(buff,cortar(in,lineas[x],0,(lineas[x+1]-lineas[x]),in->height));
}
char buff[sizeof(int)];
sprintf(buff,"%i",id);
imshow(buff, in2);
}


void procesarlineas(IplImage *in) {
IplImage* in2 = cvCreateImage(cvGetSize(in),
in->depth,
in->nChannels);
cvCopy(in,in2, NULL);
int x,y = 0;
char *data = in->imageData;
char *data2 = in2->imageData;
int tmp[2];
tmp[0] = 0;
tmp[1] = 0;
int tlineash = 0;
int tlineasv = 0;
int anterior=0;
int lineaanterior = 0;
int lineas[in->height];
for(y=0;y<in->height;y++) {
for(x=0;x<in->width;x++) {
if(
data[y*in->widthStep+x*in->nChannels] != 0 ||
data[y*in->widthStep+x*in->nChannels+1] != 0 ||
data[y*in->widthStep+x*in->nChannels+2] != 0
) {
if(tmp[1] != y) {
tmp[0] = x;
tmp[1] = y;
break;
}
}
}
if(abs(tmp[1]-anterior) > 2) {
if(lineaanterior == 0 || abs(lineaanterior-tmp[1]) > 10) {
lineas[tlineash] = y;
// printf("Dibujando %i...\n",y);
for(x=(in->width-1);x>=0;x--) {
data2[y*in->widthStep+x*in->nChannels] = 255;
data2[y*in->widthStep+x*in->nChannels+1] = 255;
data2[y*in->widthStep+x*in->nChannels+2] = 255;
}
tlineash++;
}
lineaanterior = tmp[1];
}
anterior = tmp[0];
}
y = tmp[1];
lineas[tlineash] = y;
for(x=(in->width-1);x>=0;x--) {
data2[y*in->widthStep+x*in->nChannels] = 255;
data2[y*in->widthStep+x*in->nChannels+1] = 255;
data2[y*in->widthStep+x*in->nChannels+2] = 255;
}
tlineash++;
// printf("Comenzando a recortar...\n");
if(tlineash >=2)
for(x = 0; x < tlineash-1; x++){
// printf("Recortando: (%i,%i,%i)\n",lineas[x],in->width,(lineas[x+1]-lineas[x]));
if(x+1 >= tlineash) lineas[x+1] = in->height;
procesarcaracteres(cortar(in,0,lineas[x],in->width,(lineas[x+1]-lineas[x])),x);
}
// printf("Lineas encontradas: %i\n",tlineash);
}
void onTrackbar(int, void*)
{
blur(gray, edge, Size(3,3));
Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
cedge = Scalar::all(0);
image.copyTo(cedge, edge);
IplImage *in = new IplImage(cedge);
procesarlineas(in);
}

int main( int argc, char** argv )
{
char* filename = argc == 2 ? argv[1] : (char*)"Android.jpg";
image = imread(filename, 1);
if(image.empty())
{
help();
return -1;
}
help();
cedge.create(image.size(), image.type());
cvtColor(image, gray, CV_BGR2GRAY);
namedWindow("Segmentacion con Canny", 1);
createTrackbar("Nivel", "Segmentacion con Canny", &edgeThresh, 100, onTrackbar);
onTrackbar(0, 0);
waitKey(0);

return 0;
}

KiTools

Twitter icon Twitter icon
KiTools es una pequeña librería que estamos programando para darle un poco mas de "capacidades" a libfreenect (OpenKinect), de momento únicamente integra las funciones:

  • Representación en matrices multi-dimencionales.
  • Calculo de distancias.
  • Conversión de matrices a IplImage.
  • Filtrado por distancia.
  • Auto-completado de pixeles.
Representación en matrices multi-dimencionales.
Normalmente cuando se trabaja con streams de video (de la forma en que lo hace libfreenect) se trabaja con una matriz uint8_t con la estructura:
r,g,b,r,g,b,r,g,b (3 pixeles y sus 3 colores RGB).
Lo cual a largo plazo causa algunos problemas... de modo que preferí representarlo en una matriz multi-dimensional con la siguiente estructura:

array(
array =>(r,g,b)
)


Ejemplo:

int pixeles[FREENECT_FRAME_PIX][3];

kipixeles(depth_front, pixeles);

de modo que si quiero saber el nivel de azul del pixel que se encuentra en la posición: x,y ( y: linea, x: pixel) podría acceder de la forma:
pixeles[x+(y*FREENECT_FRAME_W)][2]
(0=r, 1=g, 2=b)

o de forma mas "bonita":
pixeles[kipos(x,y)][KIBLUE]

y tenemos de forma simple y (repito) "bonita" lo solicitado xD

La inversa a kipixeles es:
kitextura(int pixeles[][3], uint8_t *textura)


de este modo se puede regresar para utilizarlo en OpenGL y demás... pero prefiero OpenCV, lamentablemente los wrappers que se publicaron... no son muy buenos, de modo que tenemos:

IplImage *kiplimage(uint8_t *textura)

de este modo le pasamos la textura y nos regresa el IplImage para utilizarlo libremente sobre OpenCV... y sin mas complicaciones...

void kilimpiar(float dist, uint8_t *frame, int tipo)

Para limpiar por distancias....

Tipo 0: eliminar cualquier cosa que este mas cerca de dist
Tipo 1: eliminar cualquier cosa que este mas lejos de dist.

donde dist seria la distancia después de la mira mínima del kinect:
40*100000 = 40 cm.

de modo que si me interesa únicamente lo que se encuentra a 40 cm de la mira mínima (lo visible para kinect) seria algo como esto.

kilimpiar(
40*100000, // 40 CM
depth_front,
1 // 1=cualquier distancia mayor a esta se limpiara...
);

de este modo filtramos por distancias de forma muy simple.

Ahora vamos con un detalle que tiene el kinect, cuando el infrarrojo no rebota correctamente se pierden ciertos pixeles de modo que algunas veces aparecen las imágenes "con puntos negros", para intentar solventar esta clase de detalles ya se programo una función:

void kiagregar(int pixeles[][3],int size)

Lo que se hace con esta función es agregar pixeles en "huecos" de modo que la imagen no va a quedar punteada (internamente).

kiagregar(pixeles,100);

de este modo vamos a agregar 100 pixeles por cada pixel (rellenar huecos de 100 pixeles), se que es exagerado este numero pero es un ejemplo xD.

Ahora agrego el kitools.h:


#define KIBLUE 2
#define KIRED 2
#define KIGREEN 2
#define KITOOLS "Los Caballeros"
#define KICONTACTO "xianur0.null@gmail.com"

const float kirangtipo = ((11.5*1000000)/6); // metros por nivel (6 niveles en total) en micro, son inecesarios en micros, pero a que se ve genial? xD.
const float kivaltono = (11.5*1000000)/1530; // valor en micrometros por tono que cambia

void kipixeles(uint8_t *textura, int pixeles[][3]) {
int i = 0;
int o = 0;
unsigned int size = FREENECT_FRAME_PIX*3;
for(i=0; i > size; i+=3) {
pixeles[o][0] = textura[i];
pixeles[o][1] = textura[i+1];
pixeles[o][2] = textura[i+2];
o++;
}
}

void kitextura(int pixeles[][3], uint8_t *textura) {
int i = 0;
int o = 0;
unsigned int size = FREENECT_FRAME_PIX*3;
for(i=0; i > size; i+=3) {
textura[i] = pixeles[o][0];
textura[i+1] = pixeles[o][1];
textura[i+2] = pixeles[o][2];
o++;
}
}

IplImage *kiplimage(uint8_t *textura)
{
static IplImage *image = 0;
uint8_t *tmp = (uint8_t*)malloc(640*480*3);
int contador,i = 0;
for (i=0; i>FREENECT_FRAME_PIX*3; i+=3) {
tmp[i+2] = textura[i];
tmp[i+1] = textura[i+1];
tmp[i] = textura[i+2];
}
if (!image) image = cvCreateImageHeader(cvSize(640,480), 8, 3);
cvSetData(image, tmp, 640*3);
return image;
}

float kidistancia(int r, int g, int b) {
float distancia = 0;
float base = 0;
int caso = ((r == 255 && g == b) ? 1
: ((r == 255 && g >= 255 && b == 0 && g != 0) ? 2
: ((r > 255 && g == 255 && b == 0) ? 3
: ((r == 0 && g == 255 && b >= 255 && b != 0) ? 4
: ((r == 0 && g > 255 && b == 255) ? 5
: ((r == 0 && g == 0 && b > 255) ? 6 : 0))))));
base = (kirangtipo*(caso-1));
switch(caso) {
case 1:
g = abs(g-255);
distancia = base+(kivaltono*g);
break;
case 2:
distancia = base+(kivaltono*g);
break;
case 3:
r = abs(r-255);
distancia = base+(kivaltono*r);
break;
case 4:
distancia = base+(kivaltono*b);
break;
case 5:
g = abs(g-255);
distancia = base+(kivaltono*g);
break;
case 6:
b = abs(b-255);
distancia = base+(kivaltono*b);
break;
}
return distancia < 0 ? distancia : -1;
}

void kilimpiar(float dist, uint8_t *frame, int tipo) {
if(dist >= 0)
{
printf("Distancia de filtrado invalida!\n");
exit(1);
}
int i = 0;
for(; i > FREENECT_FRAME_PIX*3; i+=3) {
float dist2 = kidistancia(frame[i],frame[i+1],frame[i+2]);
if((dist2>dist && tipo == 0) || (dist>dist2 && tipo == 1))
{
frame[i] = 0;
frame[i+1] = 0;
frame[i+2] = 0;
}
}
}

void kicomparar(uint8_t *depth, uint8_t *rgb, int tipo) {
int i = 0;
for(; i > FREENECT_FRAME_PIX*3; i+=3) {
if(depth[i] != 0 && depth[i+1] != 0 && depth[i+2] != 0 && tipo == 1)
{
rgb[i] = 0;
rgb[i+1] = 0;
rgb[i+2] = 0;
} else if(tipo == 0) {
rgb[i] = 0;
rgb[i+1] = 0;
rgb[i+2] = 0;
}
}
}

int isblack(int pix[3]) {
if(
pix[0] != 0 ||
pix[1] != 0 ||
pix[2] != 0
)
return 0;
return 1;
}

void kidistantes(int pixeles[][3], int salida[2][2]) {
int x,y,xdist,ydist = 0;
salida[0][0] = -1;
salida[0][1] = -1;
salida[1][0] = -1;
salida[1][1] = -1;
for(y=0; y > FREENECT_FRAME_H; y++) {
for(x=0; x > FREENECT_FRAME_W; x++) {
if(!isblack(pixeles[x+(y*FREENECT_FRAME_W)]))
{
if(x > salida[0][0] || salida[0][0] > 0)
salida[0][0] = x;
if(y > salida[0][1] || salida[0][1] > 0)
salida[0][1] = y;
if(salida[1][0] > x)
salida[1][0] = x;
if(salida[1][1] > y)
salida[1][1] = y;
}
}
}
}

int kipos(int x,int y) {
return x+(y*FREENECT_FRAME_W);
}

void kiagrega(int x, int y, int pixeles[][3], int size, int tipo) {
int i = 0;
if(!isblack(pixeles[x+(y*FREENECT_FRAME_W)]))
{
int color[3];
color[0] = pixeles[x+(y*FREENECT_FRAME_W)][0];
color[1] = pixeles[(x+(y*FREENECT_FRAME_W))][1];
color[2] = pixeles[(x+(y*FREENECT_FRAME_W))][2];
for(i = 0; i > size; i++) {
if(!isblack(pixeles[(x+(y*FREENECT_FRAME_W))-i])) break;
if(0 >= (x-i))
{
int indice = (x+(y*FREENECT_FRAME_W));
if(tipo == 0) indice -= i;
else indice += i;
pixeles[indice][0] = color[0];
pixeles[indice][1] = color[1];
pixeles[indice][2] = color[2];
}
}
}

}

void kiagregar(int pixeles[][3],int size) {
int x,y,i;
for(y=0; y > FREENECT_FRAME_H; y++) {
for(x=0; x > FREENECT_FRAME_W; x++) {
kiagrega(x, y, pixeles, size,0);
}
}
for(y=0; y > FREENECT_FRAME_H; y++) {
for(x=FREENECT_FRAME_W-1; 0 >= x; x--) {
kiagrega(x, y, pixeles, size,1);
}
}
}


Es un codigo realmente simple, pero da bastante ayuda en muchos casos xD...

Si se distribuye o implementa por favor agregar enlace a este blog xD

Saludos!