#include <stdio.h>
#include <tchar.h>
#include <iostream>
using namespace std;

#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include "cxtypes.h"

#include "funkce_1.h"
#include <math.h>



int main( int argc, char *argv[] )
	{	
	//argv[1] = "prog2.jpg";			//zakomentovano jen pro tvorbu Debug aplikace (exe)
	IplImage *img1 = cvLoadImage( argv[1] , CV_LOAD_IMAGE_GRAYSCALE );    //načtení obrázku - příkaz CV_LOAD_IMAGE_GRAYSCALE načte rovnou v Grayscale
	IplImage *img2 = cvCreateImage( cvGetSize(img1), IPL_DEPTH_8U , 1);
	IplImage *img3 = cvCreateImage( cvGetSize(img1), IPL_DEPTH_8U , 1);
	IplImage *img4 = cvCreateImage( cvGetSize(img1), IPL_DEPTH_8U , 3);
	IplImage *img5 = cvCreateImage( cvGetSize(img1), IPL_DEPTH_8U , 3);
	cvNamedWindow( "Image_::1::__Vstupní obrázek", 0 );						// CV_WINDOW_AUTOSIZE=originální velikost
	cvNamedWindow( "Image_::2::__Obrázek po prahování", 0 );	
	cvNamedWindow( "Image_::3::__Detekované hrany v obrázku", 0 );	
	cvNamedWindow( "Image_::4::__Obrázek po houghově tranformaci a aproximaci čar", 0 );
	cvNamedWindow( "Image_::5::__Obrázek se souřadnicovou sítí a body pro odečítání barvy", 0 );
	cvMoveWindow( "Image_::1::__Vstupní obrázek" , 0 , 0 );						//obrázek do rohu
	cvMoveWindow( "Image_::2::__Obrázek po prahování" , 70 , 10 );
	cvMoveWindow( "Image_::3::__Detekované hrany v obrázku" , 140 , 20 );
	cvMoveWindow( "Image_::4::__Obrázek po houghově tranformaci a aproximaci čar" , 210 , 30 );
	cvMoveWindow( "Image_::5::__Obrázek se souřadnicovou sítí a body pro odečítání barvy" , 280 , 40 );
	cvResizeWindow( "Image_::1::__Vstupní obrázek" , ((img1->width)*650)/(img1->height), 650  );
	cvResizeWindow( "Image_::2::__Obrázek po prahování" , ((img2->width)*650)/(img2->height), 650  );
	cvResizeWindow( "Image_::3::__Detekované hrany v obrázku" , ((img3->width)*650)/(img3->height), 650  );
	cvResizeWindow( "Image_::4::__Obrázek po houghově tranformaci a aproximaci čar" , ((img4->width)*650)/(img4->height), 650  );
	cvResizeWindow( "Image_::5::__Obrázek se souřadnicovou sítí a body pro odečítání barvy" , ((img5->width)*650)/(img5->height), 650  );



//_______________THRESHOLDING_________________________________________________
	
	cvThreshold( img1, img2, 100, 255, CV_THRESH_BINARY );		
	//cvSmooth(img,img2, CV_MEDIAN, 3, 0, 0, 0);				//nepotřebuji
	cvMerge(img2,img2,img2,NULL, img5);		// potrebuji vytvorit z 1kanálového obrazu 3kanálový - spojím tedy 3 jednokanálové


//_______________HRANY - CANNYHO DETEKTOR____(sobel)______________________

	cvCanny( img1, img3, 100 , 150 );
	//cvSobel(img,img3,1,0 ,3);
	//cvSobel(img,img3,1,0,3);


	CvMemStorage* storage = cvCreateMemStorage(0);	
	int * CarySvisle =(int *) new (int[5000]);
	int * CaryVodorovne =(int *) new int[5000];
	int * CaraX =(int *) new int[5000];
	int * CaraY =(int *) new int[5000];

//__________________________________Houghova transformace_________________________
	//provedu hledání čar v obraze
	//nalezené čáry rozdělím podle toho zda jsou svislé nebo vodorovné a nastavím jim odlišné barvy barvy
	CvSeq* lines = cvHoughLines2( img3, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 10, 5, 10 );
        for( int i = 0; i < lines->total; i++ )
			{
            CvPoint* line_coords = (CvPoint *) cvGetSeqElem(lines, i);
			int dx = line_coords[0].x - line_coords[1].x;
			int dy = line_coords[0].y - line_coords[1].y;
			if (abs(dx) < abs(dy))
				{
				static int j=0;
				cvLine( img4, line_coords[0], line_coords[1], CV_RGB(255, 0, 0), 1, 8 );
				CarySvisle[j]= line_coords[0].x;			//zde si uložím počáteční souřadnici každé svislé čáry
				j++;
				}
				else
					{
					static int k=0;
					cvLine( img4, line_coords[0], line_coords[1], CV_RGB(0, 255, 0), 1, 8 );
					CaryVodorovne[k]= line_coords[0].y;		//zde si uložím počáteční souřadnici každé vodorovné čáry
					k++;
					}
			}


//____________Aproximace čar nalezených Houghovou transformací____________________

	int pocetX=0;
	int pocetY=0;
	CaraX = Hough_aprox(CarySvisle, pocetX, img4, 'x');
	CaraY = Hough_aprox(CaryVodorovne, pocetY, img4, 'y');


//__________________Selektování čar po aproximaci_________________________________

	CaraX = Cary_selektovani(CaraX, pocetX);
	CaraY = Cary_selektovani(CaraY, pocetY);


//______________________Vykreslení souřadnicové sítě______________________________

	CvPoint cara[2];	
	for (int h = 0; h <= 21 ; h++)
		{
		//cara v ose X
		cara[0].x = cara[1].x = CaraX[h];
		cara[0].y = CaraY[0];
		cara[1].y = CaraY[21];
		cvLine( img5, cara[0] , cara[1] , CV_RGB(255, 0, 0), 1, 8 );
		//cara v ose Y
		cara[0].y = cara[1].y = CaraY[h];
		cara[0].x = CaraX[0];
		cara[1].x = CaraX[21];
		cvLine( img5, cara[0] , cara[1] , CV_RGB(255, 0, 0), 1, 8 );
		}


//___________________Výpočet souřadnic bodů ~= uprostřed modulů______________________

	int * BodyX = (int *) new int[31];
	int * BodyY = (int *) new int[31];
	for (int h = 0; h < 21 ; h++)
		{
		BodyX[h] = CaraX[h] + ( (CaraX[h+1] - CaraX[h]) /2 );
		BodyY[h] = CaraY[h] + ( (CaraY[h+1] - CaraY[h]) /2 );
		}


//____________Kreslení křížků - znázorňují bod kde se čte barva - log0 nebo log1_____
	
	for (int x = 0; x < 21 ; x++)
		{
		for (int y = 0; y < 21 ; y++)
			{
			nakresli_krizek(BodyX[x], BodyY[y], img5);
			}
		}



//_______________Zobrazení obrázků____________________________________________________________

	cvShowImage( "Image_::1::__Vstupní obrázek", img1 );
	cvShowImage( "Image_::2::__Obrázek po prahování", img2 );
	cvShowImage( "Image_::3::__Detekované hrany v obrázku", img3 );
	cvShowImage( "Image_::4::__Obrázek po houghově tranformaci a aproximaci čar", img4 );
	cvShowImage( "Image_::5::__Obrázek se souřadnicovou sítí a body pro odečítání barvy", img5 );
	//cvWaitKey( 0 );							// 0 znamená nekonečný čas pro čekání, jinak lze vložit číslo v ms

  

//______________Alokace paměti pro matice____________________________________________

	int m=21;	//počet řádků  
	int n=21;	//počet sloupců
	int **matice = new int* [m];		// tvorba matice odectene z obrazu
	  for(int i = 0; i < m; i++)
	    matice[i] = new int[n];
	int **maska = new int* [m];			// tvorba matice masky QR kódu
	  for(int i = 0; i < m; i++)
	    maska[i] = new int[n];
	int **kod_bezmasky = new int* [m];	//tvorba matice vlastních dat (bez masky)
	  for(int i = 0; i < m; i++)
	    kod_bezmasky[i] = new int[n];

	if(!matice){cerr << "Chyba při alokování paměti\n";return 1;}
	if(!maska){cerr << "Chyba při alokování paměti\n";return 1;}
	if(!kod_bezmasky){cerr << "Chyba při alokování paměti\n";return 1;}


//_________________Tvorba matice log1 a log 0 z obrazu_______________________________

	CvScalar c;
	for (int x = 0; x < 21 ; x++)
		{
		for (int y = 0; y < 21 ; y++)
			{
			c = cvGet2D(img2, BodyY[y], BodyX[x]);
			if (c.val[0] == 255)
				{matice[x][y]=0;}
			else
				{matice[x][y]=1;}
			}
		}


//______________Výpis matice do konzoly______________________________________________

	cout << "Vypis matice vyctene z obrazku QR kodu\n";
	vypis_matice (matice);


//_____________Funkce pro čtení typu masky a korekce chyb____________________________

	int * maska_korekce = (int *) new int[2];		//návr. hodnota z funkce maskaAkorekce-první prvek je korekce, druhý maska
	maska_korekce = maskaAkorekcechyb (matice);


//_________________Výpis úrovně korekce chyb_________________________________________

	cout  << "\n\n\n\nDetekovana korekce chyb je cislo:  " << maska_korekce[0];
	switch (maska_korekce[0])
		{
		case 0: cout  <<"\nKorekce chyb je urovne M";break;
		case 1: cout  <<"\nKorekce chyb je urovne L";break;
		case 2: cout  <<"\nKorekce chyb je urovne H";break;
		case 3: cout  <<"\nKorekce chyb je urovne M";break;
		default: cout  <<"\nChybně odečtena korekce chyb!!!";break;
		}


//________________Výpis čísla masky__________________________________________________
	cout  <<"\n\n\n\nDetekovana maska je cislo:  " << maska_korekce[1];

//_______________Tvorba masky, podle toho jaká byla odečtena_________________________
//_______________hned také odebrání masky od QR kódu_________________________________
	
	for (int i = 0; i < 21 ; i++)
		{
		int a;
		for (int j = 0; j < 21 ; j++)
			{
			switch (maska_korekce[1])					//přepínání mezi typy masek
					{
					case 0: a = (i+j) % 2; break;
					case 1: a = i % 2; break;
					case 2: a = j % 3; break;
					case 3: a = (i+j) % 3; break;
					case 4: a = ((i/2) + (j/3)) % 2; break;
					case 5: a = (i*j) % 2 + (i*j) % 3; break;
					case 6: a = ((i*j) % 2 + (i*j) % 3) % 2; break;
					case 7: a = ((i+j) % 2 + (i*j) % 3) % 2; break;
					}
			if (a == 0)
				{maska[j][i] = 1;}
			else
				{maska[j][i] = 0;}
			}
		}

	for (int i = 0; i < 21 ; i++)									//odebrání masky
		{for (int j = 0; j < 21 ; j++)
			{kod_bezmasky[i][j] = matice[i][j] ^ maska[i][j]; } }	//zůstávají vlastní data


//___________Výpis matic do konzoly__________________________________________________

	cout << "\nVypis masky\n";
	vypis_matice (maska);

	cout << "\n\n\n\n\n\nVypis kodu bez masky\n";
	vypis_matice (kod_bezmasky);


//____________Indikátor módu jsou první data uložená v QR kódu_______________________
//_______________ - algoritmus je zatím omezen jen na alfanumerický typ dat__________

	int indikator_modu;
	indikator_modu = kod_bezmasky[21-1][21-1] * 8 + kod_bezmasky[20-1][21-1] * 4 + kod_bezmasky[21-1][20-1] * 2 + kod_bezmasky[20-1][20-1] * 1  ;
	cout  << "\n\nDetekovany indikator modu je:  " << indikator_modu;
	if (indikator_modu == 2)
		{cout  << "\nTo znamena alfanumericky" ;}
	else {cout  << "\nNeni alfanumericky-algoritmus podporuje jen alfanumericky typ" ;}


//____________Indikátor počtu znaků následuje hned za indikátorem módu_______________
//_______________ - je to důležité abychom věděli kam až sahají data_________________

	int indikator_poctu_znaku=0;
	indikator_poctu_znaku = kod_bezmasky[21-1][19-1] * 256 + kod_bezmasky[20-1][19-1] * 128 + kod_bezmasky[21-1][18-1] * 64 + kod_bezmasky[20-1][18-1] * 32  + kod_bezmasky[21-1][17-1] * 16 + kod_bezmasky[20-1][17-1] * 8  + kod_bezmasky[21-1][16-1] * 4 + kod_bezmasky[20-1][16-1] * 2 + kod_bezmasky[21-1][15-1] * 1 ;
	cout  << "\n\nDetekovany indikator poctu znaku je:  " << indikator_poctu_znaku;


//____________Nyní načtu do pole data[], všechny možné informace, které mohou být daty________(korekci chyb neřeším)
//_______________ - objem dat odpovídá QR kódu s nejnižší úrovní korekce chyb => nejvíce codeword obsahující data
//_______________ - později si vyberu jen potřebná data podle počtu znaků atd...______________

	int * data = (int *) new int[400];
	data = odecti_jen_data (kod_bezmasky);



//____Nyní určím z počtu znaků - jestli je počet lichý________________________________________
//______ - pokud ano - vložím do bit11 počet dělen 2, a do bit6 dám jedničku__________________
//______ - pokud ne - vložím do bit11 počet dělen 2, a do bit6 dám nulu_______________________
//_________ - bit11 - v každých 11 bitech jsou uloženy dva znaky - proto počet znaků / 2  ____
//_________ - bit6 - v případě lichého počtu znaků je poslední (lichý) znak uložen v posledních 6 bitech dat (již nikoliv 11)

	int bit11, bit6;
	if ( indikator_poctu_znaku % 2 == 0 )
		{bit11 = indikator_poctu_znaku / 2;
		bit6 = 0; }
	else
		{bit11 = indikator_poctu_znaku / 2;
		bit6 = 1; }
	


//__________Nyní odečtu vlastní zakódované znaky____v celočíselné hodnotě_____________________
	
	int * znaky = (int *) new int[100];
	znaky = data_na_znaky (data, bit11, bit6);


//__________Vypíši znaky v celočíselném tvaru_________________________________________________

	cout  << "\n\n";
	for (int i = 0; i < (2*bit11+bit6) ; i++)
		{
			cout  << "\nZnak " << i << ": " << znaky[i];
		}


//__________Vypíši VÝSLEDNÉ ZNAKY - v ALFANUMERICKÉM tvaru____________________________________

	cout  << "\n\nDekodovane znaky:  ";

	for (int i = 0; i < (2*bit11+bit6) ; i++)
		{
		vypsat_znak (znaky[i]);
		}




//_______________Ukládání obrázků_____________________________________________________________
	
	cvSaveImage( "obr_01_prahovany_snimek.png", img2 );
	cvSaveImage( "obr_02_detekovane hrany.png", img3 );
	cvSaveImage( "obr_03_cary_hough_plus_aproximace.png", img4 );
	cvSaveImage( "obr_04_souradnicova_sit_plus_krizky.png", img5 );


//______________Úklidové práce________________________________________________________________	

	cvWaitKey( 0 );							// 0 znamená nekonečný čas pro čekání, jinak lze vložit číslo v ms
	//getchar();
	cvReleaseImage( &img1 );
	cvReleaseImage( &img2 );
	cvReleaseImage( &img3 );
	cvReleaseImage( &img4 );
	cvReleaseImage( &img5 );
	cvDestroyWindow( "Image_::1::__Vstupní obrázek" );
	cvDestroyWindow( "Image_::2::__Obrázek po prahování" );
	cvDestroyWindow( "Image_::3::__Detekované hrany v obrázku" );
	cvDestroyWindow( "Image_::4::__Obrázek po houghově tranformaci a aproximaci čar" );
	cvDestroyWindow( "Image_::5::__Obrázek se souřadnicovou sítí a body pro odečítání barvy" );
	cvReleaseMemStorage( &storage );
	delete [ ] CarySvisle;
	delete [ ] CaryVodorovne;
	delete [ ] CaraX;
	delete [ ] CaraY;
	delete [ ] BodyX;
	delete [ ] BodyY;
	delete [ ] matice;
	delete [ ] maska;
	delete [ ] kod_bezmasky;
	delete [ ] maska_korekce;
	delete [ ] data;
	delete [ ] znaky;

	return 0;
	}
