Getting more than a one pixel value in a single contour - c++

Hello after connected component labeling for a single label why am I getting more than a one pixel value ?
this is my image
#include <iostream>
#include <vector>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
void FindBlobs(const cv::Mat &binary, std::vector < std::vector<cv::Point2i> > &blobs);
int main(int argc, char **argv)
{
cv::Mat img = cv::imread("/Users/Rodrane/Documents/XCODE/test/makalesvm/persembe.png", 0); if(!img.data) {
std::cout << "File not found" << std::endl;
return -1;
}
cv::namedWindow("binary");
cv::namedWindow("labelled");
cv::Mat output = cv::Mat::zeros(img.size(), CV_8UC3);
cv::Mat binary;
std::vector < std::vector<cv::Point2i > > blobs;
equalizeHist( img , img );
cv::threshold(img, binary, 0, 1, cv::THRESH_BINARY_INV);
FindBlobs(binary, blobs);
// Randomy color the blobs
for(size_t i=0; i < blobs.size(); i++) {
unsigned char r = 255 * (rand()/(1.0 + RAND_MAX));
unsigned char g = 255 * (rand()/(1.0 + RAND_MAX));
unsigned char b = 255 * (rand()/(1.0 + RAND_MAX));
for(size_t j=0; j < blobs[i].size(); j++) {
int x = blobs[i][j].x;
int y = blobs[i][j].y;
output.at<cv::Vec3b>(y,x)[0] = b;
output.at<cv::Vec3b>(y,x)[1] = g;
output.at<cv::Vec3b>(y,x)[2] = r;
}
}
cout << "H = "<< endl << " " << output << endl << endl;
cv::imshow("binary", img);
cv::imshow("labelled", output);
cv::waitKey(0);
return 0;
}
void FindBlobs(const cv::Mat &binary, std::vector < std::vector<cv::Point2i> > &blobs)
{
blobs.clear();
// Fill the label_image with the blobs
// 0 - background
// 1 - unlabelled foreground
// 2+ - labelled foreground
cv::Mat label_image;
binary.convertTo(label_image, CV_32SC1);
int label_count = 2; // starts at 2 because 0,1 are used already
for(int y=0; y < label_image.rows; y++) {
int *row = (int*)label_image.ptr(y);
for(int x=0; x < label_image.cols; x++) {
if(row[x] != 1) {
continue;
}
cv::Rect rect;
cv::floodFill(label_image, cv::Point(x,y), label_count, &rect, 0, 0, 4);
std::vector <cv::Point2i> blob;
for(int i=rect.y; i < (rect.y+rect.height); i++) {
int *row2 = (int*)label_image.ptr(i);
for(int j=rect.x; j < (rect.x+rect.width); j++) {
if(row2[j] != label_count) {
continue;
}
blob.push_back(cv::Point2i(j,i));
}
}
blobs.push_back(blob);
label_count++;
}
}
}
actually this is a single label when I print the image
however when I check pixel values. I see there is actually 2 different values (I just write down part of the matrix not all)
H = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 33, 0, 192, 33, 0, 192, 33, 0, 0, 0, 0, ]
also by adding this line of code to the last line of FindBlobs function I recieve 3 since the label_count variable starts from 2 this also proves that H is a single label.
cout << "number of labels = "<< endl << " " << label_count << endl << endl;

Related

GSL complex matrix - eigenvalues/eigenvectors

I've written a program which is computing the eigenvalues and eigenvectors of a hermitian matrix.
Does anyone know how this is done in GSL properly? Here is what I already have.
//hermitian matrix
0 1 0 -i
1 0 -i 0
0 i 0 1
i 0 1 0
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_eigen.h>
using namespace std;
const int N = 4;
int main(){
gsl_eigen_hermv_workspace *workN = gsl_eigen_hermv_alloc(N);
gsl_matrix_complex *A = gsl_matrix_complex_alloc(N, N);
gsl_complex i = gsl_complex_rect(0.0,1.0);
gsl_complex ii = gsl_complex_rect(0.0,-1.0);
gsl_vector *eval = gsl_vector_alloc(N);
gsl_matrix_complex *evec = gsl_matrix_complex_alloc(N, N);
double mTab[] = {
0, 1, 0, 5,
1, 0, 5, 0,
0, 5, 0, 1,
5, 0, 1, 0
};
gsl_matrix_complex_view tmpM = gsl_matrix_complex_view_array(mTab, N, N);
gsl_matrix_complex_memcpy(A, &tmpM.matrix);
gsl_matrix_complex_set(A, 0, 3, ii);
gsl_matrix_complex_set(A, 1, 2, ii);
gsl_matrix_complex_set(A, 2, 1, i);
gsl_matrix_complex_set(A, 3, 0, i);
gsl_eigen_hermv(A, eval, evec, workN);
for(int i=0; i < N; i++){
for(int j=0; j < N; j++){
gsl_complex z = gsl_matrix_complex_get(A, i, j);
cout << GSL_REAL(z) << "+ i" << GSL_IMAG(z) << " ";
}
cout << "\n";
}
cout << "\n";
for(int i=0; i < N; i++){
cout << gsl_vector_get(eval, i) << " ";
}
return 0;
}
This is how I output the eigenvectors
for(int i=0; i < N; i++){
for(int j=0; j < N; j++){
gsl_complex z = gsl_matrix_complex_get(A, i, j);
cout << GSL_REAL(z) << "+ i" << GSL_IMAG(z) << " ";
}
cout << "\n";
}
Finally, here's the way I declared the matrix in question.
double mTab[] = {
0, 1, 0, 5,
1, 0, 5, 0,
0, 5, 0, 1,
5, 0, 1, 0
};
Later, I added the complex numbers.
I managed to print the eigenvectors but I don't know how to do that for the eigenvalues. Any help with that is appreciated?.
You have an error in using the double mTab for gsl_matrix_complex_view_array. This assumes an array of complex numbers represented as real parts followed by imaginary parts in one large array of double values. You can change your definition to:
double mTab[] = {
0, 0, 1, 0, 0, 0, 5, 0,
1, 0, 0, 0, 5, 0, 0, 0,
0, 0, 5, 0, 0, 0, 1, 0,
5, 0, 0, 0, 1, 0, 0, 0,
};
(Which also means you don't need to use a "dummy" variable of 5 just to rewrite it by ±i later.) Then the code for printing eigenvalues works well.
Also you have a typo in the eigenvector printing loop: it should be
gsl_complex z = gsl_matrix_complex_get(evec, i, j);
not
gsl_complex z = gsl_matrix_complex_get(A, i, j);

SVM training issue for a simple dataset (Opencv 2.4.9)

I'm trying a simple example to learn SVM in OpenCV, I'm not getting the right support vectors after training. Need some help in understanding the issue.
My code is :
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
using namespace cv;
using namespace std;
int main() {
Mat frame(Size(640,360), CV_8UC3, Scalar::all(255));
float train[15][2] = { {296, 296}, {296, 312}, {312, 8}, {312, 56}, {312, 88}, {328, 88}, {328, 104}, {328, 264}, {344, 8}, {344, 40}, {360, 8}, {360, 56}, {376, 8}, {376, 40}, {376, 56} };
Mat trainingDataMat(15, 2, CV_32FC1, train);
float labels[15] = { -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1 };
Mat labelsMat(15, 1, CV_32FC1, labels);
CvSVMParams param;
param.svm_type = CvSVM::C_SVC;
param.C = 0.1;
param.kernel_type = SVM::LINEAR;
param.term_crit = TermCriteria(CV_TERMCRIT_ITER, 1000, 1e-6);
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), param);
cout<< "Training Finished..." << endl;
for(int i = 0; i < frame.rows; ++i) {
for(int j = 0; j < frame.cols; ++j) {
Mat sampleMat = (Mat_<float>(1,2) << i,j);
float response = SVM.predict(sampleMat);
//cout << response << endl;
if(response == 1) {
frame.at<Vec3b>(i,j)[2] = 0;
} else {
frame.at<Vec3b>(i,j)[0] = 0;
}
}
}
for(int dis = 0; dis < trainingDataMat.rows; dis++) {
if(labels[dis] == 1) {
circle(frame, Point((int)train[dis][0], (int)train[dis][1]), 3, Scalar (0, 0, 0), -1);
} else {
circle(frame, Point((int)train[dis][0], (int)train[dis][1]), 3, Scalar (0, 255, 0), -1);
}
}
int n = SVM.get_support_vector_count();
for(int i = 0; i < n; i++) {
const float* v = SVM.get_support_vector(i);
cout << "support Vectors : " << v[0] << " " << v[1] <<endl;
circle(frame,Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), 2, 8);
}
imwrite("frame.jpg",frame);
imshow("output", frame);
waitKey(0);
return 0;
}
Output image is attached
The SVM line is not separating the two classes as I expect.
Result for Support Vector is
support Vectors : 0 0.0125
The SVM should be OK. I think the problem lies in your display. When you call your circle(frame, Point((int)train[dis][0], (int)train[dis][1]), 3, Scalar (0, 0, 0), -1);, OpenCV understands that you want a circle in row number train[dis][1] and column number train[dis][0]. This is not what you want because a specificity of OpenCV is that it uses different coordinate systems for matrices and points. image.at<float>(Point(i,j)) is equivalent to image.at<float>(j,i).
Try replacing your circle calls with this:
if(labels[dis] == 1) {
circle(frame, Point((int)train[dis][1], (int)train[dis][0]), 3, Scalar (0, 0, 0), -1);
} else {
circle(frame, Point((int)train[dis][1], (int)train[dis][0]), 3, Scalar (0, 255, 0), -1);
}

How to determine the value of the 3 channel in RGB or BGR

This is the image that i need to detect
// cdst is the image
// This is the code which i want to know what are the values in a certain pixel but the code below only detects the first then the 2nd and 3rd is equal to 0
void dec()
{
Mat em;
cdst="path of the image";
//this is the value of BGR per color that i wish to check
Vec3f red(0, 0, 255);
Vec3f blue(255, 0, 0);
Vec3f green(0, 128, 0);
Vec3f yellow(0, 255, 255);
Vec3f marron(0, 0,128);
Vec3f pink(147, 20, 255);
Vec3f indigo(130, 0, 75);
Vec3f midblue(112, 25, 25);
Vec3f magenta(139, 0, 139);
//em will hold the cdst image
em=cdst;
//for loop to determine what are the colors
for (int i = 0; i < l.size(); i++)
{
int x = l[i][0];
int y = l[i][1];
cout << x << " " << y<<endl;
Vec3f px = em.at<uchar>(y,x);
//Im trying to print all color of the 3 channels
//But it only the first on has a value then the second and third is 0
cout << px.val[0] << " " << px.val[1] << " "<<px.val[2]<<endl;
if (px == pink)
{
cout<<"A";
}
}
}
How about try this code?
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(){
Mat em(100, 100, CV_8UC3, Scalar::all(0));
rectangle(em, Rect(30, 30, 10, 10), Scalar(147, 20, 255), -1);
imshow("em", em);
waitKey(1);
Vec3b pink(147,20,255);
for (int i = 0; i < em.rows; i++){
for (int j = 0; j < em.cols; j++){
Vec3b px = em.at<Vec3b>(i, j);
cout << px << endl;
if (px == pink){
cout << "A" ;
}
}
}
waitKey(0);
destroyAllWindows();
return 0;
}

Transfer a 2D array to a csv file?

I have a matrix called "temp_matrix" that looks kind of like this:
0.0,100.0,100.0,100.0,
0.0,45.1,60.6,66.2,0,
0.0,45.1,60.6,66.2,0,
0.0,100.0,100.0,100.0,0,
...except mine is a 20x20 matrix.
I've tried:
ofstream excel_plate("plate.csv");
excel_plate.write(temp_matrix[0],20);
excel_plate.close();
cout << endl;
but can't compile.
I've tried:
ofstream excel_plate("plate.csv");
excel_plate << temp_matrix[0], 20, 20;
excel_plate.close();
...but just get a csv file with a string of characters in the very first cell that looks like this: 0052EE98.
Here is my code:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <math.h>
#include <Windows.h>
#include <algorithm>
#include <string>
#include <array>
#undef max
using namespace std;
const double neighbors = 4;
// START FUNCTION: Update Elements
void average(double *temp_matrix, int ROWS, int COLUMNS)
{
int i = 0;
int j = 0;
double row_left = 0;
double row_right = 0;
double column_up = 100;
double column_down = 0;
double sum = 0;
double av = 0;
for (int i = 1; i < ROWS - 1; i++)
{
for (int j = 1; j < COLUMNS - 1; j++)
{
/*
cout << "row_left:" << *(temp_matrix + i*ROWS + (j - 1)) << " ";
cout << "row_right:" << *(temp_matrix + i*ROWS + (j + 1)) << " ";
cout << "column_up:" << *(temp_matrix + (i - 1)*ROWS + j) << " ";
cout << "column_down:" << *(temp_matrix + (i + 1)*ROWS + j) << " ";
*/
row_left = *(temp_matrix + i*ROWS + (j-1));
row_right = *(temp_matrix + i*ROWS + (j + 1));
column_up = *(temp_matrix + (i-1)*ROWS + j);
column_down = *(temp_matrix + (i+1)*ROWS + j);
sum = row_left + row_right + column_up + column_down;
av = sum / neighbors;
*(temp_matrix + i*ROWS + j) = av;
}
}
}
// END FUNCTION: Update Elements
// START FUNCTION: Updtate Until Stable
void update(double *temp_matrix, int ROWS, int COLUMNS)
{
int i = 0;
int j = 0;
double row_left = 0;
double row_right = 0;
double column_up = 100;
double column_down = 0;
double sum = 0;
double old_num = 0;
double new_num = 0;
double difference = 0;
double max_change = .11;
while (max_change > .1)
{
max_change = -1;
for (int i = 1; i < ROWS - 1; i++)
{
for (int j = 1; j < COLUMNS - 1; j++)
{
old_num = *(temp_matrix + i*ROWS + j);
/*
cout << "row_left:" << *(temp_matrix + i*ROWS + (j - 1)) << " ";
cout << "row_right:" << *(temp_matrix + i*ROWS + (j + 1)) << " ";
cout << "column_up:" << *(temp_matrix + (i - 1)*ROWS + j) << " ";
cout << "column_down:" << *(temp_matrix + (i + 1)*ROWS + j) << " ";
*/
row_left = *(temp_matrix + i*ROWS + (j - 1));
row_right = *(temp_matrix + i*ROWS + (j + 1));
column_up = *(temp_matrix + (i - 1)*ROWS + j);
column_down = *(temp_matrix + (i + 1)*ROWS + j);
sum = row_left + row_right + column_up + column_down;
new_num = sum / neighbors;
difference = new_num - old_num;
if (difference > max_change)
{
max_change = difference;
}
*(temp_matrix + i*ROWS + j) = new_num;
}
}
}
}
// END FUNCTION: Update Until Stable
// START FUNCTION: Print Array
void print_array(double *temp_matrix, int ROWS, int COLUMNS)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
if (j > 0)
{
cout << ",";
}
cout << fixed << setw(5) << setprecision(1) << *(temp_matrix + i*ROWS + j);
}
cout << endl;
}
}
// END FUNCTION: Print Array
// START FUNCTION: Print 4 Excel
void print_excel(double *temp_matrix, int ROWS, int COLUMNS)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
cout << fixed << setprecision(1) << *(temp_matrix + i*ROWS + j) << ",";
}
cout << endl;
}
}
// END FUNCTION: Print 4 Excel
int main()
{
const int ROWS = 20;
const int COLUMNS = 20;
double temp_matrix[ROWS][COLUMNS] =
{
{ 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0 }
};
print_array(temp_matrix[0], 20, 20);
system("pause");
cout << endl << endl;
average(temp_matrix[0], 20, 20);
print_array(temp_matrix[0], 20, 20);
system("pause");
cout << endl << endl;
update(temp_matrix[0], 20, 20);
print_array(temp_matrix[0], 20, 20);
system("pause");
cout << endl << endl;
print_excel(temp_matrix[0], 20, 20);
ofstream excel_plate("plate.csv");
if (excel_plate.is_open())
{
excel_plate << temp_matrix[0], 20, 20;
excel_plate.close();
}
else
{
cout << "Unable to open file.";
}
/*
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
if (j > 0)
{
cout << ",";
}
cout << temp_matrix[i][j];
}
cout << endl;
}
*/
system("pause");
return 0;
}
This should be very, very simple. Please kindly help a noob out. :)
P.S. Because this is an assignment, you'll notice that I print out several matrices. The last one I print out is the format in which the matrix needs to be transferred to a .csv file.
The simplest way would be something like the following (assuming you can use c++11)
std::ofstream out("test.csv");
for (auto& row : temp_matrix) {
for (auto col : row)
out << col <<',';
out << '\n';
}
So, this code conatains a functions that should be able to transfer any 2d string array into a csv file
#include <iostream>
#include <fstream>
#include<string>
/* This is for a string array, but if you want to use any other type, just replace
'std::string' with int,double....*/
template <size_t row, size_t col>
void twodarray2csv(std::string(&array)[row][col], std::string filename)
{
std::ofstream myfile;
myfile.open(filename);
for (size_t i = 0; i < row; ++i)
{
for (size_t j = 0; j < col; ++j)
if (j < (col - 1)) {
myfile << array[i][j] << ",";
}
else if (j == (col - 1)) {
myfile << array[i][j] << "\n";
}
}
}
Th template gets the row size and column size and also opens ofstream which is basically for just opening the csv file that will be the output file
int main() {
//example
std::string ArrayName[9][3];
/* this for loop is just there to populate the array, if you have an array
already skip this step */
#include <iostream>
#include <fstream>
#include<string>
/* This is for a string array, but if you want to use any other type, just replace
'std::string' with int,double....*/
template <size_t row, size_t col>
void twodarray2csv(std::string(&array)[row][col], std::string filename)
{
std::ofstream myfile;
myfile.open(filename);
for (size_t i = 0; i < row; ++i)
{
for (size_t j = 0; j < col; ++j)
if (j < (col - 1)) {
myfile << array[i][j] << ",";
}
else if (j == (col - 1)) {
myfile << array[i][j] << "\n";
}
}
}
So the function above uses a template to get the row size and column size of the array, after which it iterates through the array so that when it passes by elements of the same row it adds a comma to the file and at the end of each row it adds a new line (\n) which is a row separator
The part below is an example of how to use it and how it works
First i just created a an Array called ArrayName, and populated it with just some numbers (int number)
int number = 0;
for (int i = 0; i < 9; i++) {
for (int x = 0; x < 3; x++) {
ArrayName[i][x] = number;
number++;
}
}
//this part converts the array to a csv file of desried name
twodarray2csv(ArrayName, "outputfile.csv");
return 0;
}
The output will be a csv file with the name "outputfile.csv" stored in the same folder with the cpp file you have

InvalidCursor error from multi-threaded SDL application

I rewrote Lode's raycasting tutorial code to make it process events in a separate thread. I found out that any SDL calls that call xlib functions need to be the main thread, so in this code all functions that rely on xlib are in the main thread.
This is the error I still get randomly from the application:
X Error of failed request: BadCursor (invalid Cursor parameter)
Major opcode of failed request: 95 (X_FreeCursor)
Resource id in failed request: 0x4a0000b
Serial number of failed request: 108
Current serial number in output stream: 107
Sometimes when I run it I'll get this error, but if I run it again it will work.
I'm not for sure how else I need to change the code because all of the graphics processing is in the main thread, the separate thread only deals with events processing. Does anybody know what I'm doing wrong?
raycaster.cpp
#include <iostream>
#include <cmath>
#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "game.hpp"
using std::cout;
static int SCREENW = 500;
static int SCREENH = 500;
static int BPP = 32;
int events_loop(void* data);
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_EVENTTHREAD);
TTF_Init();
SDL_Thread* events;
Game_state* gs = new Game_state();
events = SDL_CreateThread(events_loop, (void*)gs);
SDL_Surface* screen = SDL_SetVideoMode(SCREENW, SCREENH, BPP, SDL_SWSURFACE);
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
SDL_WM_SetCaption("Raycaster (non-textured)", NULL);
Game* game = new Game(screen, SCREENW, SCREENH, BPP);
//BEGIN GAME VARIABLES
//game map
int map[Game::MAP_WIDTH][Game::MAP_WIDTH] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 4, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
//direction variables
double pos_x = Game::PLAYER_START_X;
double pos_y = Game::PLAYER_START_Y;
double dir_x = -1; double old_dir_x;
double dir_y = 0;
int map_x, map_y;
//timing variables
double start_ticks = 0;
double end_ticks = 0;
double frame_time = 0;
//camera varibales
double camera_x;
double ray_pos_x, ray_pos_y;
double ray_dir_x, ray_dir_y;
double plane_x = 0; double plane_y = Game::FOV; double old_plane_x;
int line_height;
//DDA variables
double side_dist_x, side_dist_y;
double delta_dist_x, delta_dist_y;
double perpen_wall_dist;
int step_x, step_y;
bool EW_side; //east west side hit, negative implies north south side
bool hit = false;
//drawing variables
int draw_low_y, draw_high_y;
int r, g, b;
//movement variables
double move_speed, rotation_speed;
//BEGIN RENDERING LOGIC
while(gs->over == false) {
start_ticks = SDL_GetTicks();
//lock screen to modify its pixels
/*if(SDL_MUSTLOCK(screen)) {
SDL_LockSurface(screen);
}*/
game->clear_screen();
//BEGIN DRAWING PIXELS
for(int x = 0; x < SCREENW; x++) {
//set up camera
camera_x = 2 * x / (double(SCREENW) - 1);
ray_pos_x = pos_x; ray_pos_y = pos_y;
ray_dir_x = dir_x + plane_x * camera_x;
ray_dir_y = dir_y + plane_y * camera_x;
delta_dist_x = sqrt(1 + (ray_dir_y * ray_dir_y) / (ray_dir_x * ray_dir_x));
delta_dist_y = sqrt(1 + (ray_dir_x * ray_dir_x) / (ray_dir_y * ray_dir_y));
//what box are we in?
map_x = int(ray_pos_x); map_y = int(ray_pos_y);
//calculate step and side_dist
if(ray_dir_x < 0) {
step_x = -1;
side_dist_x = (ray_pos_x - map_x) * delta_dist_x;
}
else {
step_x = 1;
side_dist_x = (map_x + 1.0 - ray_pos_x) * delta_dist_x;
}
if(ray_dir_y < 0) {
step_y = -1;
side_dist_y = (ray_pos_y - map_y) * delta_dist_y;
}
else {
step_y = 1;
side_dist_y = (map_y + 1.0 - ray_pos_y) * delta_dist_y;
}
//step using DDA until a wall is hit
hit = false;
while(hit == false) {
if(side_dist_x < side_dist_y) {
side_dist_x += delta_dist_x;
map_x += step_x;
EW_side = false;
}
else {
side_dist_y += delta_dist_y;
map_y += step_y;
EW_side = true;
}
if(map[map_x][map_y] > 0) { hit = true; }
}
//calculate dist from camera to wall that was hit
if(EW_side == false) {
perpen_wall_dist = fabs((map_x - ray_pos_x + (1 - step_x) / 2) / ray_dir_x);
}
else {
perpen_wall_dist = fabs((map_y - ray_pos_y + (1 - step_y) / 2) / ray_dir_y);
}
//calculate line height from perpendicular wall distance
line_height = abs(int(SCREENH / perpen_wall_dist));
//calculate how high to draw the line
draw_high_y = -line_height / 2 + SCREENH / 2;
if(draw_high_y < 0) { draw_high_y = 0; }
draw_low_y = line_height / 2 + SCREENH / 2;
if(draw_low_y >= SCREENH) { draw_low_y = SCREENH - 1; }
if(draw_low_y < 0) { draw_low_y = 0; } //added (shouldn't need to be here)
//finally draw the line
game->draw_line(x, draw_low_y, draw_high_y, map[map_x][map_y], EW_side);
}
//unlock screen for blitting
/*if(SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen);
}*/
//calculate timing and print the FPS
end_ticks = SDL_GetTicks();
frame_time = (end_ticks - start_ticks) / 1000.0;
game->blit_fps(frame_time);
game->blit_location(map_x, map_y);
if(SDL_Flip(screen) != 0) {
cout << "ERROR: couldn't draw to the screen <" << SDL_GetError() << ">\n";
}
//BEGIN CALCULATING NEXT STEP
//calculate new direction based on frames drawn
move_speed = frame_time * Game_state::MOVEMENT_MULTIPLIER;
rotation_speed = frame_time * Game_state::ROTATION_MULTIPLIER;
//process movement for next frame
if(gs->movement_forward == Game_state::MOVE_UP) {
if(map[int(pos_x + dir_x * move_speed)][int(pos_y)] == 0) { pos_x += dir_x * move_speed; }
if(map[int(pos_x)][int(pos_y + dir_y * move_speed)] == 0) { pos_y += dir_y * move_speed; }
}
else if(gs->movement_forward == Game_state::MOVE_DOWN) {
if(map[int(pos_x - dir_x * move_speed)][int(pos_y)] == 0) { pos_x -= dir_x * move_speed; }
if(map[int(pos_x)][int(pos_y - dir_y * move_speed)] == 0) { pos_y -= dir_y * move_speed; }
}
if(gs->movement_side == Game_state::MOVE_RIGHT) {
old_dir_x = dir_x;
dir_x = dir_x * cos(-rotation_speed) - dir_y * sin(-rotation_speed);
dir_y = old_dir_x * sin(-rotation_speed) + dir_y * cos(-rotation_speed);
old_plane_x = plane_x;
plane_x = plane_x * cos(-rotation_speed) - plane_y * sin(-rotation_speed);
plane_y = old_plane_x * sin(-rotation_speed) + plane_y * cos(-rotation_speed);
}
else if(gs->movement_side == Game_state::MOVE_LEFT) {
old_dir_x = dir_x;
dir_x = dir_x * cos(rotation_speed) - dir_y * sin(rotation_speed);
dir_y = old_dir_x * sin(rotation_speed) + dir_y * cos(rotation_speed);
old_plane_x = plane_x;
plane_x = plane_x * cos(rotation_speed) - plane_y * sin(rotation_speed);
plane_y = old_plane_x * sin(rotation_speed) + plane_y * cos(rotation_speed);
}
}
delete gs;
delete game;
return 0;
}
int events_loop(void* data) {
Game_state* gs = (Game_state*)data;
SDL_Event evt;
while(1) {
while(SDL_PollEvent(&evt)) {
if(evt.type == SDL_QUIT) { gs->over = true; cout << "quit\n"; return 0; }
else if(evt.type == SDL_KEYDOWN) {
if(evt.key.keysym.sym == SDLK_w) {
gs->move(Game_state::MOVE_UP);
}
else if(evt.key.keysym.sym == SDLK_s) {
gs->move(Game_state::MOVE_DOWN);
}
else if(evt.key.keysym.sym == SDLK_a) {
gs->move(Game_state::MOVE_LEFT);
}
else if(evt.key.keysym.sym == SDLK_d) {
gs->move(Game_state::MOVE_RIGHT);
}
else if(evt.key.keysym.sym == SDLK_ESCAPE) { gs->over = true; cout << "escape\n"; return 0; }
}
else if(evt.type == SDL_KEYUP) {
if(evt.key.keysym.sym == SDLK_w) {
gs->stop_move(Game_state::MOVE_UP);
}
else if(evt.key.keysym.sym == SDLK_s) {
gs->stop_move(Game_state::MOVE_DOWN);
}
else if(evt.key.keysym.sym == SDLK_a) {
gs->stop_move(Game_state::MOVE_LEFT);
}
else if(evt.key.keysym.sym == SDLK_d) {
gs->stop_move(Game_state::MOVE_LEFT);
}
}
else { /* ignore */ }
}
}
}
game.cpp
#include <iostream>
#include "game.hpp"
using std::cout; using std::endl;
Game_state::Game_state() {
movement_forward = NO_MOVE;
movement_side = NO_MOVE;
over = false;
}
void Game_state::move(int direction) {
switch(direction) {
case NO_MOVE:
break;
case MOVE_UP:
if(movement_forward == MOVE_DOWN) { movement_forward = NO_MOVE; }
else { movement_forward = MOVE_UP; }
break;
case MOVE_DOWN:
if(movement_forward == MOVE_UP) { movement_forward = NO_MOVE; }
else { movement_forward = MOVE_DOWN; }
break;
case MOVE_LEFT:
if(movement_side == MOVE_RIGHT) { movement_side = NO_MOVE; }
else { movement_side = MOVE_LEFT; }
break;
case MOVE_RIGHT:
if(movement_side == MOVE_LEFT) { movement_side = NO_MOVE; }
else { movement_side = MOVE_RIGHT; }
break;
default:
cout << "ERROR: invalid movement in Game_state::move() at time " << SDL_GetTicks() << endl;
break;
}
}
void Game_state::stop_move(int direction) {
switch(direction) {
case NO_MOVE:
break;
case MOVE_UP:
case MOVE_DOWN:
movement_forward = NO_MOVE;
break;
case MOVE_RIGHT:
case MOVE_LEFT:
movement_side = NO_MOVE;
break;
default:
cout << "ERROR: invalid movement in Game_state::stop_move() at time " << SDL_GetTicks() << endl;
break;
}
}
Game::Game(SDL_Surface* scr, int w, int h, int b) {
screen = scr;
scr_w = w; scr_h = h; bpp = b;
//fps printing vars
fps_location.x = 0; fps_location.y = 0;
fps_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 24);
fps_color.r = 0; fps_color.g = 0; fps_color.b = 255; //blue
//location printing vars
location_font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf", 18);
//determine how high the font surface should be from the bottom
location_color.r = 0; location_color.g = 90; location_color.b = 240;
location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
location_location.x = 0; location_location.y = scr_h - location_surface->clip_rect.h;
//set up the wall colors
wall_color[OUTSIDE_WALL].r = 255; wall_color[OUTSIDE_WALL].g = 255; wall_color[OUTSIDE_WALL].b = 255;
wall_color[RED_WALL].r = 255; wall_color[RED_WALL].g = 0; wall_color[RED_WALL].b = 0;
wall_color[GRAY_WALL].r = 160; wall_color[GRAY_WALL].g = 160; wall_color[GRAY_WALL].b = 160;
wall_color[GOLD_WALL].r = 232; wall_color[GOLD_WALL].g = 211; wall_color[GOLD_WALL].g = 34;
}
void Game::clear_screen() {
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
}
void Game::blit_fps(double frame_time) {
sprintf(fps_buffer, "FPS: %3.3f", 1.0 / frame_time);
fps_surface = TTF_RenderText_Solid(fps_font, fps_buffer, fps_color);
if(SDL_BlitSurface(fps_surface, NULL, screen, &fps_location) != 0) {
cout << "ERROR: couldn't blit the FPS surface <" << SDL_GetError() << ">\n";
}
}
void Game::blit_location(int x, int y) {
sprintf(location_buffer, "location: %d, %d", x, y);
location_surface = TTF_RenderText_Solid(location_font, location_buffer, location_color);
if(SDL_BlitSurface(location_surface, NULL, screen, &location_location) != 0) {
cout << "ERROR: couldn't blit the location surface <" << SDL_GetError() << ">\n";
}
}
//high_y means the y coord closest to the top of the screen
void Game::draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side) {
//cout << "high_y = " << high_y << " low_y = " << low_y << endl;
int r = wall_color[wall_type].r;
int g = wall_color[wall_type].g;
int b = wall_color[wall_type].b;
if(EW_side == true) { r /= 2; g /= 2; b /= 2; }
//cout << "r = " << r << " g = " << g << " b = " << b << "\n";
//draw ceiling
/*for(int y = 0; y < high_y - 1; y++) {
put_pixel(x, y, 0, 255, 90);
}*/
for(int y = high_y; y <= low_y; y++) {
put_pixel(x, y, r, g, b);
}
//draw floor (checkered)
/*for(int y = low_y + 1; y <= scr_h; y++) {
if(x % 20 > 10 && y % 20 > 10) {
put_pixel(x, y, 255, 255, 255);
}
}*/
}
void Game::put_pixel(int x, int y, int r, int g, int b) {
int bpp = screen->format->BytesPerPixel;
Uint8* p = (Uint8*)screen->pixels + y * screen->pitch + x * bpp;
Uint32 pixel = SDL_MapRGB(screen->format, r, g, b);
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16*)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
}
else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32*)p = pixel;
break;
}
}
game.hpp
#include "SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
class Game_state {
public:
//movement statics
static const int NO_MOVE = 0;
static const int MOVE_UP = 1;
static const int MOVE_DOWN = 2;
static const int MOVE_LEFT = 3;
static const int MOVE_RIGHT = 4;
static const double MOVEMENT_MULTIPLIER = 5.0;
static const double ROTATION_MULTIPLIER = 3.0;
int movement_forward;
int movement_side;
bool over;
Game_state();
void move(int direction);
void stop_move(int direction);
};
class Game {
private:
//fps vars
char fps_buffer[50];
TTF_Font* fps_font;
SDL_Surface* fps_surface;
SDL_Rect fps_location;
SDL_Color fps_color;
//location vars
char location_buffer[24];
TTF_Font* location_font;
SDL_Surface* location_surface;
SDL_Rect location_location;
SDL_Color location_color;
void put_pixel(int x, int y, int r, int g, int b);
public:
//game statics
static const int MAP_WIDTH = 20;
static const int MAP_HEIGHT = 20;
static const double FOV = 0.66;
static const int PLAYER_START_X = 1;
static const int PLAYER_START_Y = 1;
//wall options
static const int FLOOR = 0;
static const int OUTSIDE_WALL = 1;
static const int RED_WALL = 2;
static const int GRAY_WALL = 3;
static const int GOLD_WALL = 4;
//game variables
SDL_Surface* screen;
int scr_w;
int scr_h;
int bpp;
SDL_Color wall_color[5];
Game(SDL_Surface* scr, int w, int h, int b);
void clear_screen();
void blit_fps(double frame_time);
void blit_location(int x, int y);
void draw_line(int x, int low_y, int high_y, int wall_type, bool EW_side);
};
SDL_PollEvent documentation states (clearly for SDL 1.2 at least) that it should only be called from the same thread that set the video mode.