//Exercises_10_1-2-10-11-12-15-16.cpp ćwiczenia z końca rozdziału 10
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

 
void help(const char **argv) {
	cout << "\n\n"
		<< "Ten program rozwiązuje ćwiczenia 1, 2, 10, 11, 12, 15 i 16 z rozdziału 9 \n"
		<< "Wywołanie:\n"
		<< argv[0] << " <path/image_name>\n\n"
		<< "Na przykład: ./" << argv[0] << " ../lena.jpg\n"
		<< endl;
}

int main( int argc, const char** argv )
{
	help(argv);
	if(argc < 2) {
		cout << "\nBŁĄD: za mało parametrów.\n" << endl;
		return -1;
	}
	Mat src ;
	Mat gray;
	Mat mask;
	Mat temp;
	Mat temp2;
	/************************************************************************/
	/* 1.	Załaduj obraz z ciekawymi teksturami. Wygładź go na kilka sposobów za pomocą funkcji cv::smooth() z parametrem smoothtype=cv::GAUSSIAN.
a.	Użyj symetrycznych okien filtra o wymiarach 3×3, 5×5, 9×9 oraz 11×11 i wyświetl wyniki.
b.	Czy wyniki są prawie takie same po dwukrotnym wygładzeniu obrazu za pomocą filtra gaussowskiego o wymiarach 5×5 jak po jednokrotnym wygładzeniu za pomocą filtra o wymiarach 11×11? Wyjaśnij.
                                                                     */
	/************************************************************************/	
	src = imread(argv[1]);
	if (src.empty())
	{
		cout << "\nBłąd: parametr nie jest nazwą obrazu.\n" << endl;
		return -1;
	}
	double minPixelValue, maxPixelValue;
	//a
	Mat smooth33;Mat smooth55;Mat smooth99;Mat smooth111;
	GaussianBlur(src,smooth33,cv::Size(3,3),0);
	GaussianBlur(src,smooth55,cv::Size(5,5),0);
	GaussianBlur(src,smooth99,cv::Size(9,9),0);
	GaussianBlur(src,smooth111,cv::Size(11,11),0);
	//b
	GaussianBlur(smooth55,smooth55,cv::Size(5,5),0);
	temp = smooth55 - smooth111;
	cv::minMaxIdx(temp, &minPixelValue, &maxPixelValue);
	// maxPixelVaule  = 19 , wynik "dwa filtry gaussowskie 5 × 5 " jest podobny do "filtra 11 × 11"

	/************************************************************************/
	/* 2.	Utwórz jednokanałowy obraz o wymiarach 100×100. Ustaw wszystkie piksele na 0. Następnie ustaw piksel środkowy na 255.
a.	Wygładź ten obraz przy użyciu filtra gaussowskiego o wymiarach 5×5 i wyświetl wynik. Co się okazało?
b.	Zrób to samo jeszcze raz, ale z filtrem gaussowskim o wymiarach 9×9.
c.	Jak będzie wyglądał obraz, jeśli zostanie wygładzony dwa razy za pomocą filtra o wymiarach 5×5? Porównaj go z wynikiem działania filtra o wymiarach 9×9. Czy obrazy są prawie takie same? Wyjaśnij.
	*/
	/************************************************************************/
	Mat singleChanel100 = Mat(100,100,CV_8U,Scalar(0));
	singleChanel100.at<uchar>(50,50) = 255;
	
	//a
	GaussianBlur(singleChanel100,temp,cv::Size(5,5),0);
	imshow("5 × 5 Gaussian filter",temp);
	//b
	GaussianBlur(singleChanel100,temp,cv::Size(9,9),0);
	imshow("9 × 9Gaussian filter",temp);
	//c
	GaussianBlur(singleChanel100,temp,cv::Size(5,5),0);
	GaussianBlur(temp,temp,cv::Size(5,5),0);
	GaussianBlur(singleChanel100,temp2,cv::Size(9,9),0);
	absdiff(temp,temp2,temp2);
	cv::minMaxIdx(temp2, &minPixelValue, &maxPixelValue);
	//maxPixelVaule = 5,the result are nearly the same
	/************************************************************************/
	/* 10.	Utwórz losowy obraz o niewielkiej różnicy wartości (tak skonfiguruj generator liczb losowych, aby generował liczby nie różniące się między sobą o wiele więcej niż 3 oraz aby większość z nich była bliska zeru). Załaduj ten obraz do programu z funkcjami rysowania, np. PowerPoint, i narysuj koło linii spotykających się w jednym punkcie. Otrzymany obraz przepuść przez filtr bilateralny i wyjaśnij wynik.                                                                     */
	/************************************************************************/
	Mat matLowVariance  = Mat(512,512,CV_8U,Scalar(0));
	RNG arng = cv::theRNG();
	arng.fill(matLowVariance,RNG::UNIFORM,0,30);
	// rysowanie koła linii spotykających się w środku
	line(matLowVariance,Point(256,256),Point(256,256-100),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256+100,256+100),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256+100,256),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256+100,256-100),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256,256+100),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256-100,256-100),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256-100,256),Scalar(255),1);
	line(matLowVariance,Point(256,256),Point(256-100,256+100),Scalar(255),1);
	imshow("koło linii spotykających się w punkcie centralnym",matLowVariance);
	bilateralFilter(matLowVariance,temp,5,10.0,2.0); 
	imshow("bilateralFilter",temp);
	/************************************************************************/
	/* 11.	Załaduj obraz sceny i przekonwertuj go na skalę szarości.
a.	Wykonaj morfologiczną operację Top Hat na tym obrazie i wyświetl wynik.
b.	Przekonwertuj otrzymany obraz na 8-bitową maskę.
c.	Skopiuj wartość skali szarości do oryginalnego obrazu, w którym maska Top Hat (z punktu b tego ćwiczenia) jest niezerowa. Wyświetl wynik.                                                                     */
	/************************************************************************/
	cvtColor(src,gray,COLOR_BGR2GRAY);
	//a 
	morphologyEx(gray,temp,CV_MOP_TOPHAT,Mat());
	imshow(" morphological  Top  Hat",temp);
	//b
	temp.convertTo(mask,CV_8UC1);
	//c
	cvtColor(gray,gray,COLOR_GRAY2BGR);
	gray.copyTo(src,mask);
	imshow("wynik ćwiczenia 11",src);
	/************************************************************************/
	/* 12.  12.	Załaduj obraz przedstawiający wiele drobnych elementów.
a.	Za pomocą funkcji resize() zmniejsz obraz o współczynnik 2 w każdym wymiarze (dzięki czemu obraz zmniejszy się o współczynnik 4). Zrób to trzy razy i wyświetl wynik.
b.	Teraz trzy razy zmniejsz oryginalny obraz za pomocą funkcji cv::pyrDown() i wyświetl wynik.
c.	Jaka jest różnica między dwoma otrzymanymi wynikami? Dlaczego te dwie techniki są różne?
	*/
	/************************************************************************/
	//a
	Mat matResize;
	resize(src,matResize,cv::Size(0,0),0.5,0.5);
	resize(matResize,matResize,cv::Size(0,0),0.5,0.5);
	resize(matResize,matResize,cv::Size(0,0),0.5,0.5);
	imshow("zmiana rozmiaru trzy razy",matResize);
	//b
	Mat	matPyrDown;
	pyrDown(src,matPyrDown);
	pyrDown(matPyrDown,matPyrDown);
	pyrDown(matPyrDown,matPyrDown);
	imshow("pyrDown 3 razy",matPyrDown);
	//c
	absdiff(matResize,matPyrDown,temp);
	imshow("dwa wyniki różnicy między resize i pyDown",temp);
	/************************************************************************/
	/* 15.	Za pomocą funkcji cv::filter2D() utwórz filtr wykrywający w obrazie tylko linie pod kątem 60 stopni. Wyświetl wyniki na jakiejś ciekawej scenie.                                                                     */
	/************************************************************************/
	Mat matWithLines = Mat(512,512,CV_8UC1,Scalar(0));
	// utwórz dziewięć linii
	for (int i=0;i<9;i++)
	{
		line(matWithLines,Point(arng.uniform(0,512),arng.uniform(0,521)),Point(arng.uniform(0,512),arng.uniform(0,521)),Scalar(255),1);
	}
	// linia 45 stopni
	line(matWithLines,Point(0,512),Point(512,0),Scalar(255),1);
	matWithLines.convertTo(matWithLines,CV_32FC1,1.0/255);
	// wykrywa tylko linie pod kątem 45 stopni
	Mat matKernel = Mat(3,3,CV_32FC1,Scalar(0));
    matKernel.at<float>(0,0) =  0 ;
	matKernel.at<float>(0,1) =  0 ;
	matKernel.at<float>(0,2) =  1.0/3 ;
	matKernel.at<float>(1,0) =  0 ;
	matKernel.at<float>(1,1) =  1.0/3 ;
	matKernel.at<float>(1,2) =  0 ;
	matKernel.at<float>(2,0) =  1.0/3 ;
	matKernel.at<float>(2,1) = 0;
	matKernel.at<float>(2,2) = 0;
	filter2D(matWithLines,temp,CV_32FC1,matKernel);
	threshold(temp,temp,0.99,1,CV_THRESH_BINARY);
	/************************************************************************/
	/* 16.	Jądra rozdzielne: Utwórz jądro gaussowskie o wymiarach 3×3 z wierszami [(1/16, 2/16, 1/16), (2/16, 4/16, 2/16), (1/16, 2/16, 1/16)] i punktem centralnym w środku.
a.	Przefiltruj obraz przy użyciu tego jądra i wyświetl wynik.
b.	Teraz utwórz dwa jednowymiarowe jądra z punktami centralnymi w środku — poziome (1/4, 2/4, 1/4) i pionowe (1/4, 2/4, 1/4). Załaduj ten sam oryginalny obraz co poprzednio i za pomocą funkcji cv::filter2D() dwa razy dokonaj jego splotu — raz z pierwszym jednowymiarowym jądrem i drugi raz z drugim jednowymiarowym jądrem. Opisz otrzymany wynik.
c.	Opisz stopień złożoności (liczbę operacji) dla jądra z punktu a i dla jąder z punktu b. Różnica jest efektem korzyści z użycia dwóch osobnych jąder i całej klasy filtrów Gaussa — lub każdego innego rozkładalnego liniowo filtra, który jest rozdzielny, ponieważ splot jest operacją liniową.                                                                     */
	/************************************************************************/
	Mat matGaussianKernel = Mat(3,3,CV_32FC1,Scalar(0));
	matGaussianKernel.at<float>(0,0) = 1.0/16;
	matGaussianKernel.at<float>(0,1) = 2.0/16;
	matGaussianKernel.at<float>(0,2) = 1.0/16;
	matGaussianKernel.at<float>(1,0) = 2.0/16;
	matGaussianKernel.at<float>(1,1) = 4.0/16;
	matGaussianKernel.at<float>(1,2) = 2.0/16;
	matGaussianKernel.at<float>(2,0) = 1.0/16;
	matGaussianKernel.at<float>(2,1) = 2.0/16;
	matGaussianKernel.at<float>(2,2) = 1.0/16;
	//a
	src.convertTo(temp,CV_32F,1.0/255);
	filter2D(temp,temp,CV_32F,matGaussianKernel);
	imshow("jądro gaussowskie 3 × 3",temp);
	//b
	Mat matKernel1 = Mat(1,3,CV_32FC1,Scalar(0));
	Mat matKernel2 = Mat(3,1,CV_32FC1,Scalar(0));
	matKernel1.at<float>(0,0) = 1.0/4;
	matKernel1.at<float>(0,1) = 2.0/4;
	matKernel1.at<float>(0,2) = 1.0/4;
	matKernel2.at<float>(0,0) = 1.0/4;
	matKernel2.at<float>(1,0) = 2.0/4;
	matKernel2.at<float>(2,0) = 1.0/4;
	filter2D(temp,temp2,CV_32F,matKernel1);
	filter2D(temp2,temp2,CV_32F,matKernel2);
	absdiff(temp,temp2,temp2);
	// temp i temp2 to ta sama macierz, maxPixelValue jest bardzo mała, prawie ZERO
	cv::minMaxIdx(temp2, &minPixelValue, &maxPixelValue);
	//c kolejność nie ma znaczenia
	waitKey();
	return 0;
}
