Version 18.0.1

This commit is contained in:
Antonio Cañas Vargas 2018-10-04 22:45:16 +02:00
parent bc5a70c5d1
commit e7da37b8b3
10 changed files with 15596 additions and 9 deletions

View File

@ -9,7 +9,7 @@
# and used to support university teaching. # # and used to support university teaching. #
# # # #
# This file is part of SWAD core. # # This file is part of SWAD core. #
# Copyright (C) 1999-2017 Antonio Cañas Vargas # # Copyright (C) 1999-2018 Antonio Cañas Vargas #
# # # #
# This program is free software: you can redistribute it and/or modify # # This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as # # it under the terms of the GNU Affero General Public License as #

39
fotomaton/Makefile Normal file
View File

@ -0,0 +1,39 @@
#
# FOTOMATON. Detector de rostros de la plataforma SWAD
#
# Copyright (C) 2018 Daniel J. Calandria Hernández,
# Antonio Cañas Vargas &
# Jesús Mesa González.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
BIN=../bin
SRC=.
CXX = g++
CC=g++
CFLAGS=-Wall -O3 $(shell pkg-config --cflags opencv)
CXXFLAGS = -Wall -O3 $(shell pkg-config --cflags opencv)
LDFLAGS =$(shell pkg-config --libs opencv)
TARGETS= fotomaton
all: $(TARGETS)
fotomaton: util.o fotomaton.o
$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
clean:
rm -f *.o

42
fotomaton/common.h Normal file
View File

@ -0,0 +1,42 @@
/*
* FOTOMATON. Detector de rostros de la plataforma SWAD
*
* Copyright (C) 2018 Daniel J. Calandria Hernández,
* Antonio Cañas Vargas &
* Jesús Mesa González.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __common_h
#define __common_h
//OpenCV Headers
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <cstdlib>
#include <iostream>
#include <fstream>
#define REAL float
#endif

14838
fotomaton/example/cascade.xml Normal file

File diff suppressed because it is too large Load Diff

446
fotomaton/fotomaton.cpp Normal file
View File

@ -0,0 +1,446 @@
/*
* FOTOMATON. Detector de rostros de la plataforma SWAD
*
* Copyright (C) 2018 Daniel J. Calandria Hernández,
* Antonio Cañas Vargas &
* Jesús Mesa González.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "util.h"
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
using namespace cv;
bool operator < ( const pair<REAL, Rect> &a, const pair<REAL, Rect> &b )
{
return a.first < b.first;
}
///////////////////////////////////////////////////////////
void stretch_contrast (IplImage *m, int channel, int *h, int max, int f);
void enhance_contrast (IplImage *img, float t);
void enhance_saturation (IplImage *img, float t);
void enhance_light (IplImage *img, int r, int c, float f);
void contraer (IplImage *mat, int channel, int orig, int dest, int f);
bool check_background (IplImage *img, int r, int c, int t);
void ExtractObjectImage ( IplImage *src, const Rect &r, float ratio, IplImage *dst );
///////////////////////////////////////////////////////////
int main (int argc, char **argv)
{
char file_name[512];
if (argc < 4)
{
cout << "fotomaton <classifier> <input_file> <width>" << endl;
return 2;
}
// Obtener la anchura de la imagen.
int width = atoi ( argv[3] );
if ( width <= 0)
{
cout << "Error: Width must be positive!" << endl;
return 2;
}
// Cargar el clasificador en cascada proporcionado como argumento.
CascadeClassifier cascade;
string ruta_classifier(argv[1]); // Convertir en string la ruta donde se encuentra el clasificador.
if (!cascade.load (ruta_classifier)){
cout << "Error: Classifier not found!" << endl;
return 2;
}
// Cargar imagen.
IplImage *img = cvLoadImage(argv[2]);
if (!img)
{
cout << "Error: Image cannot be read!" << endl;
return 2;
}
string str_name;
PartPath ( argv[2], 0, &str_name, 0 );
for (int i = strlen(argv[2])-1; i>= 0; i--)
if (argv[2][i] == '.')
{
argv[2][i] = '\0';
break;
}
// Detectar objetos.
Mat gray_img; // Crear una matriz donde almacenar en escala de grises la imagen leída.
Mat m_img = cvarrToMat(img);
cvtColor (m_img, gray_img, CV_BGR2GRAY); // Almacenar en la matriz anterior la imagen en escala de grises.
vector <Rect> objects; // Crear un vector en el cual almacenar los objetos detectados.
cascade.detectMultiScale(gray_img, objects, 1.09, 1);
// Si no se han detectado caras (vector de rectángulos vacío) se genera el mapa de la imagen
// sin enmascarar.
if (objects.empty())
{
IplImage *img_map = cvCreateImage (cvSize(width, (width*img->height)/img->width), 8, 3);
cvResize (img, img_map);
sprintf (file_name, "%s_map.jpg", argv[2]);
cvSaveImage (file_name, img_map);
return 1;
}
// Enmascarar la imagen y generar mapa.
if(img->width < width)
width = img->width;
IplImage *img_map = cvCreateImage (cvSize(width, (width*img->height)/img->width), 8, 3);
cvResize (img, img_map);
CvMat *mask = cvCreateMat (img_map->height, img_map->width, CV_8UC1);
cvSet (mask, cvRealScalar(1));
sprintf (file_name, "%s_map.txt", argv[2]);
ofstream map_file ( file_name );
// Extaer cada una de las imágenes y aplicar los diferentes filtros.
IplImage *img_object = cvCreateImage ( cvSize(150, 200), 8, 3 );
for (int i = objects.size()-1; i >= 0; i--)
{
int res = 1; // fondo blanco?
Rect r;
r.x = (objects[i].x * width) / img->width;
r.y = (objects[i].y * width) / img->width;
r.width = (objects[i].width * width) / img->width;
r.height = (objects[i].height * width) / img->width;
cvCircle ( mask, cvPoint(r.x + r.width/2, r.y + r.height/2), r.width*0.75+1, CV_RGB(0,0,0), -1, CV_AA );
// Extraer y mejorar imagen.
////////////////////////////////////////////////////////
ExtractObjectImage ( img, objects[i], 0.75, img_object );
sprintf (file_name, "%s_%03d_paso1.jpg", argv[2], i);
cvSaveImage (file_name, img_object);
enhance_contrast (img_object, 0.0009);
enhance_saturation ( img_object, 0.0001);
if (!check_background( img_object, int(0.07*img_object->height), int(0.1*img_object->width), 150 ) )
{
cvCircle ( img_map, cvPoint(r.x + r.width/2, r.y + r.height/2), r.width*0.75+1, CV_RGB(255,0,0), 2, CV_AA );
res = 0;
}
else
{
cvCircle ( img_map, cvPoint(r.x + r.width/2, r.y + r.height/2), r.width*0.75+1, CV_RGB(0,255,0), 2, CV_AA );
sprintf (file_name, "%s_%03d_paso2.jpg", argv[2], i);
cvSaveImage (file_name, img_object);
enhance_light (img_object, int(0.07*img_object->height),
int(0.1*img_object->width), 20.0);
sprintf (file_name, "%s_%03d_paso3.jpg", argv[2], i);
cvSaveImage (file_name, img_object);
}
////////////////////////////////////////////////////////
sprintf (file_name, "%s_%03d", str_name.c_str(), i);
map_file << int(r.x + r.width/2) << " " << int(r.y + r.height/2) << " " << int(r.width*0.75+1) << " " << res << " " << file_name << '\n';
}
map_file.close();
cvSubS ( img_map, cvScalar(80,120,120,0), img_map, mask );
sprintf (file_name, "%s_map.jpg", argv[2]);
cvSaveImage (file_name, img_map);
cvReleaseImage (&img_map);
cvReleaseImage (&img_object);
cvReleaseImage (&img);
return 0;
}
void ExtractObjectImage ( IplImage *src, const Rect &r, float ratio, IplImage *dst )
{
Rect obj_r;
obj_r = r;
obj_r.y -= r.height / 2;
obj_r.height += 1.5*r.height;
obj_r.x -= r.width / 2;
obj_r.width += r.width;
if (obj_r.y < 0)
obj_r.y = 0;
if (obj_r.x < 0)
obj_r.x = 0;
if (obj_r.y + obj_r.height >= src->height)
obj_r.height = src->height - obj_r.y;
if (obj_r.x + obj_r.width >= src->width)
obj_r.width = src->width - obj_r.x;
if (obj_r.width / obj_r.height > ratio)
{
//Añadir filas por abajo, si se puede. Si no, quitar columnas
if (obj_r.y + obj_r.width / ratio > src->height)
{
float f = obj_r.height * ratio;
obj_r.x = obj_r.x + (obj_r.width - f)/2;
obj_r.width = f;
}
else
obj_r.height = obj_r.width / ratio;
}
else
{
//Añadir columnas si se puede
float f = obj_r.height * ratio;
if (obj_r.x + (obj_r.width - f)/2 < 0 ||
obj_r.x + (obj_r.width + f)/2 > src->width )
obj_r.height = obj_r.width / ratio;
else
{
obj_r.x = obj_r.x + (obj_r.width - f)/2;
obj_r.width = f;
}
}
CvMat mat_aux;
cvGetSubRect (src, &mat_aux, obj_r);
cvResize (&mat_aux, dst);
}
/*************************************************************/
/* FILTROS DE REALCE */
/*************************************************************/
//Mejora de contraste.
void enhance_contrast (IplImage *img, float t)
{
int *hist[3];
for (int i = 0; i < 3; ++i)
{
hist[i] = new int[256];
memset (hist[i], 0, sizeof(int)*256);
}
int count = 0;
//Calcular histogramas
for (int i = 0; i < img->height; ++i)
for (int j = 0; j < img->width; ++j)
for (int k = 0; k < 3; ++k)
{
++hist[k][((uchar*) (img->imageData + i * img->widthStep))[j * img->nChannels + k]];
}
count = img->height * img->width;
//Estirar el contraste teniendo en cuenta los pixels de piel.
for (int i = 0; i < 3; ++i)
stretch_contrast (img, i, hist[i], int(t * count), 255 );
}
// Mejora de saturacion.
void enhance_saturation (IplImage *img, float t)
{
cvCvtColor(img,img, CV_RGB2HSV);
int hist[256];
int count = 0;
// Calcular histogramas.
for (int i = 0; i < img->height; ++i)
for (int j = 0; j < img->width; ++j)
{
++hist[((uchar*) (img->imageData + i*img->widthStep))[j * img->nChannels + 2]];
++count;
}
count = img->height *img->width;
// Estirar el contraste teniendo en cuenta los pixels de piel
stretch_contrast (img, 2, hist, int(t * count), 255 );
cvCvtColor(img,img, CV_HSV2RGB);
}
// Realce de blancos.
void enhance_light (IplImage *img, int r, int c, float f)
{
int avg[3] = {0,0,0};
int nuevo_valor, savg, total = 0;
// Obtener el color medio de la imagen, tomando las esquinas como referencia
// Esquina izquierda.
for (int i = 0; i <= r; ++i)
for (int j = c - (c * i) / r; j >= 0; --j)
{
++total;
for (int n = 0; n < 3; ++n)
avg[n] += ((uchar*)(img->imageData + i * img->widthStep))[j * img->nChannels + n];
}
// Esquina derecha.
for (int i = 0; i <= r; ++i)
for (int j = (c * i) / r; j <= c; ++j)
{
++total;
for (int n = 0; n < 3; ++n)
avg[n] += ((uchar*)(img->imageData + i * img->widthStep))[img->width -1 - c + j];
}
for (int i = 0; i < 3; ++i)
avg[i] /= total;
// Obtener el nuevo valor de blanco.
savg = (avg[0] + avg[1] + avg[2])/3;
nuevo_valor = int(savg + (255 - savg) / f);
//Desplazar hacia el nuevo blanco
for (int i = 0; i < 3; ++i)
contraer (img, i, avg[i], nuevo_valor, 255);
}
// Estira el histograma h asociado a m.
void stretch_contrast (IplImage *m, int channel, int *h, int max, int f)
{
int /*max = int(t * m.rows() * m.cols()),*/ sum = 0;
int inf = 0, sup = 0, w;
// Limite inferior.
for (unsigned i = 0; i < 256; ++i)
{
sum += h[i];
if (sum > max)
{ inf = i; break; }
}
// Limite superior.
sum = 0;
for (int i = 256-1; i >= 0; --i)
{
sum += h[i];
if (sum > max)
{ sup = i; break; }
}
// Estirar histograma entre [inf,sup].
w = sup - inf;
//cout << "inf = " << inf << ", sup = " << sup << ", w = " << w << endl;
for (int i = 0; i < m->height; ++i)
for (int j = 0; j < m->width; ++j)
{
if (((uchar*) (m->imageData + i * m->widthStep))[j * m->nChannels + channel] < inf)
((uchar*) (m->imageData + i * m->widthStep))[j * m->nChannels + channel] = 0;
else if (((uchar*) (m->imageData + i * m->widthStep))[j * m->nChannels + channel] > sup)
((uchar*) (m->imageData + i * m->widthStep))[j * m->nChannels + channel] = f;
else ((uchar*) (m->imageData + i * m->widthStep))[j * m->nChannels + channel] = int(rint( (((uchar*) (m->imageData + i * m->widthStep))[j * m->nChannels + channel] - inf) * f / w));
}
}
/*
* Reimplementación de la rutina contraer del proyecto de Alvarez y Rodrigo.
*/
void contraer (IplImage *mat, int channel, int orig, int dest, int f)
{
float k = dest / float(orig);
if (f > orig)
{
for (int i = 0; i < mat->height; ++i)
for (int j = 0; j < mat->width; ++j)
{
float val;
if (((uchar*) (mat->imageData + i * mat->widthStep))[j * mat->nChannels + channel] < orig)
val = ((uchar*) (mat->imageData + i * mat->widthStep))[j * mat->nChannels + channel] * k;
else
val = ((((uchar*) (mat->imageData + i * mat->widthStep))[j * mat->nChannels + channel] - orig) * (f - dest) / float(f - orig)) + dest;
// Se comprueba que este dentro del rango [0,f]
if (val < 0)
((uchar*) (mat->imageData + i * mat->widthStep))[j * mat->nChannels + channel] = 0;
else if (val > f)
((uchar*) (mat->imageData + i * mat->widthStep))[j * mat->nChannels + channel] = f;
else
((uchar*) (mat->imageData + i * mat->widthStep))[j * mat->nChannels + channel] = int(rint(val));
}
}
}
bool check_background (IplImage *img, int r, int c, int t)
{
// Comprueba los dos triangulos de las esquinas superiores.
int avg[3]; // Medias para rojo, verde y azul.
bool ok = true;
int total = 0;
// Esquina izquierda.
avg[0] = avg[1] = avg[2] = 0;
// Calcular media.
for (int i = 0; i <= r; ++i)
for (int j = c - (c * i) / r; j >= 0; --j)
{
++total;
avg[0] += ((uchar*) (img->imageData + i * img->widthStep))[j * img->nChannels ];
avg[1] += ((uchar*) (img->imageData + i * img->widthStep))[j * img->nChannels + 1];
avg[2] += ((uchar*) (img->imageData + i * img->widthStep))[j * img->nChannels + 2];
}
avg[0] /= total;
avg[1] /= total;
avg[2] /= total;
ok = (avg[0] + avg[1] + avg[2]) > 3*t;
if (!ok) //el fondo es oscuro
return false;
// Esquina derecha.
avg[0]= avg[1] = avg[2] = total = 0;
for (int i = 0; i <= r; ++i)
for (int j = (c * i) / r; j <= c; ++j)
{
++total;
avg[0] += ((uchar*) (img->imageData + i * img->widthStep))[(img->width - 1 - c + j) * img->nChannels ];
avg[1] += ((uchar*) (img->imageData + i * img->widthStep))[(img->width - 1 - c + j) * img->nChannels + 1];
avg[2] += ((uchar*) (img->imageData + i * img->widthStep))[(img->width - 1 - c + j) * img->nChannels + 2];
}
avg[0] /= total;
avg[1] /= total;
avg[2] /= total;
ok = (avg[0] + avg[1] + avg[2]) > 3*t;
return ok;
}

148
fotomaton/util.cpp Normal file
View File

@ -0,0 +1,148 @@
/*
* FOTOMATON. Detector de rostros de la plataforma SWAD
*
* Copyright (C) 2018 Daniel J. Calandria Hernández,
* Antonio Cañas Vargas &
* Jesús Mesa González.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "util.h"
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
std::vector<std::string> ReadDir (const char *dir_name, const char *ext)
{
std::vector<std::string> names;
//DIR *dir;
dirent *dir_ent, **ents;
char *path;
std::vector<std::string> exts;
//Ajustar path correctamente
path = new char[strlen(dir_name)+2];
strcpy (path,dir_name);
if (dir_name[strlen(dir_name)-1] != '/')
strcat(path, "/");
//Obtener extensiones
for (unsigned i = 0; i < strlen(ext); )
{
if (ext[i] != ' ')
{
exts.push_back(std::string(&ext[i], 3));
i = i+3;
}
else i++;
}
//dir = opendir (dir_name);
/*if (!dir)
{
std::cerr << "(utilReadDir) Error: no se puede abrir el directorio de entrada" << std::endl;
return std::vector<std::string>();
}*/
int count = scandir(dir_name, &ents, 0, alphasort);
if (count < 0)
{
std::cerr << "(ReadDir) Error: no se puede abrir el directorio de entrada" << std::endl;
return std::vector<std::string>();
}
//while ( (dir_ent = readdir(dir)) )
for (int i = 0; i < count; i++)
{
dir_ent = ents[i];
bool res = false;
for (unsigned i = 0; i < exts.size(); i++)
{
if (!strncmp (&dir_ent->d_name[strlen(dir_ent->d_name) - 3], exts[i].c_str(), 3))
{
res = true;
break;
}
}
if (res)
names.push_back (std::string(path)+dir_ent->d_name);
}
//closedir(dir);
delete [] path;
free(ents);
return names;
}
void PartPath (const std::string& pfull, std::string* path, std::string* name, std::string* ext)
{
int file_pos = -1;
int ext_pos = -1;
char *full = new char[strlen(pfull.c_str())+1];
strcpy (full, pfull.c_str());
// Localizar extension y archivo
for (int i = strlen(full)-1; i >= 0; i--)
{
if (full[i] == '.' && ext_pos < 0)
{
ext_pos = i+1;
full[i] = '\0';
}
else if (full[i] == '/' && file_pos < 0)
{
file_pos = i+1;
full[i] = '\0';
}
if (file_pos > -1 && ext_pos > -1)
break;
}
if (path)
*path = full;
if (name)
{
if (file_pos >= 0)
*name = full + file_pos;
else
*name = full;
}
if (ext)
{
if (ext_pos >= 0)
*ext = full + ext_pos;
else
*ext = "";
}
delete [] full;
}
void FullPath (const std::string &path, const std::string &name, const std::string &ext, std::string &full)
{
full = path;
if (path[path.length()-1] != '/')
full = full + "/";
full = full + name + "." + ext;
}

71
fotomaton/util.h Normal file
View File

@ -0,0 +1,71 @@
/*
* FOTOMATON. Detector de rostros de la plataforma SWAD
*
* Copyright (C) 2018 Daniel J. Calandria Hernández,
* Antonio Cañas Vargas &
* Jesús Mesa González.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef util_h
#define util_h
// Algunas funciones utiles
#include "common.h"
#include <string>
#include <vector>
#include <cstdlib>
std::vector<std::string> ReadDir (const char *dir, const char *ext);
void FullPath (const std::string &path, const std::string &name, const std::string &ext, std::string &full);
void PartPath (const std::string &full, std::string *path, std::string *name, std::string *ext);
inline double randu ()
{ return std::rand() / (RAND_MAX + 1.0);
}
inline double randu (double a, double b)
{
return a + (b - a) * randu();
}
inline int rand (int a, int b)
{
return a + (b - a) * randu();
}
inline CvMat* ExtractPatch ( const CvMat *img, const CvRect& r, const CvSize& size = cvSize(0,0) )
{
if (r.x + r.width > img->width) std::cout << "ERROR\n";
if (r.y + r.height > img->height) std::cout << "ERROR\n";
CvMat orig_patch;
cvGetSubRect (img, &orig_patch, r);
/*if (size.width == 0 || size.height == 0)
return orig_patch;*/
CvMat *patch = cvCreateMat (size.width, size.height, img->type);
cvResize ( &orig_patch, patch );
//cvReleaseMat (&orig_patch);
return patch;
}
#endif

View File

@ -359,6 +359,7 @@ En OpenSWAD:
ps2pdf source.ps destination.pdf ps2pdf source.ps destination.pdf
*/ */
/* /*
Version 18.0.1: Oct 04, 2018 My courses are highlighted in listing of courses of current degree. (234738 lines)
Version 18.0: Oct 04, 2018 New version of fotomaton, programmed by Daniel Calandria and Jesús Mesa. Version 18.0: Oct 04, 2018 New version of fotomaton, programmed by Daniel Calandria and Jesús Mesa.
Changes in code to avoid new warnings with GCC 7.3. (234736 lines) Changes in code to avoid new warnings with GCC 7.3. (234736 lines)
Version 17.29: Apr 24, 2018 Code refactoring and bug fixing related to actions. (234579 lines) Version 17.29: Apr 24, 2018 Code refactoring and bug fixing related to actions. (234579 lines)

View File

@ -1244,8 +1244,10 @@ static bool Crs_ListCoursesOfAYearForSeeing (unsigned Year)
TxtClassNormal = "DAT"; TxtClassNormal = "DAT";
TxtClassStrong = "DAT_N"; TxtClassStrong = "DAT_N";
} }
BgColor = (Crs->CrsCod == Gbl.CurrentCrs.Crs.CrsCod) ? "LIGHT_BLUE" :
Gbl.ColorRows[Gbl.RowEvenOdd]; /* Check if this course is one of my courses */
BgColor = (Usr_CheckIfIBelongToCrs (Crs->CrsCod)) ? "LIGHT_BLUE" :
Gbl.ColorRows[Gbl.RowEvenOdd];
/* Put green tip if course has users */ /* Put green tip if course has users */
fprintf (Gbl.F.Out,"<tr>" fprintf (Gbl.F.Out,"<tr>"

View File

@ -1278,7 +1278,7 @@ void Gam_GetDataOfGameByCod (struct Game *Game)
break; break;
case Sco_SCOPE_CRS: // Course case Sco_SCOPE_CRS: // Course
Game->Status.IBelongToScope = Usr_CheckIfIBelongToCrs (Game->Cod) && Game->Status.IBelongToScope = Usr_CheckIfIBelongToCrs (Game->Cod) &&
Gam_CheckIfICanDoThisGameBasedOnGrps (Game->GamCod); Gam_CheckIfICanDoThisGameBasedOnGrps (Game->GamCod);
break; break;
} }
@ -1287,11 +1287,11 @@ void Gam_GetDataOfGameByCod (struct Game *Game)
/* Can I answer game? */ /* Can I answer game? */
Game->Status.ICanAnswer = (Game->NumQsts != 0) && Game->Status.ICanAnswer = (Game->NumQsts != 0) &&
Game->Status.Visible && Game->Status.Visible &&
Game->Status.Open && Game->Status.Open &&
Game->Status.IAmLoggedWithAValidRoleToAnswer && Game->Status.IAmLoggedWithAValidRoleToAnswer &&
Game->Status.IBelongToScope && Game->Status.IBelongToScope &&
!Game->Status.IHaveAnswered; !Game->Status.IHaveAnswered;
/* Can I view results of the game? /* Can I view results of the game?
Can I edit game? */ Can I edit game? */