#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;

Mat getCbCrHist(const Mat& src, const int* channels);
Mat getHSVHist(const Mat& src, const int* channels);
void cvtCartesian2PolarCV_32F(const Mat& src, Mat& dst);

int main(int argc, char* argv[]) {
	Mat src;
	if (argc > 1)
		src = imread(argv[1], IMREAD_COLOR);
	else
		src = imread("fallback.png", IMREAD_COLOR);
	if (src.empty()) {
		cerr << "Image load failed" << endl;
		return -1;
	}
	
	Mat ycrcb(src.rows, src.cols, src.type());
	Mat hsv(src.rows, src.cols, src.type());
	cvtColor(src, ycrcb, COLOR_BGR2YCrCb);
	cvtColor(src, hsv, COLOR_BGR2HSV);

	int channels_Cb_Cr[] = { 1, 2 };
	Mat Cb_CrHist = getCbCrHist(ycrcb, channels_Cb_Cr);
	int channels_HSV[] = { 1, 0 };
	Mat HSHist = getHSVHist(hsv, channels_HSV);
	Mat HSPolar(src.rows, src.cols, src.type());
	cvtCartesian2PolarCV_32F(HSHist, HSPolar);

	imshow("src", src);
	Cb_CrHist.convertTo(Cb_CrHist, CV_8U);
	imshow("Histogram in Cb-Cr cartesian coordinates", Cb_CrHist);
	HSHist.convertTo(HSHist, CV_8U);
	// resize(HSHist, HSHist, Size(512, 512));
	imshow("Histogram in H-S cartesian coordinates", HSHist);
	HSPolar.convertTo(HSPolar, CV_8U);
	imshow("Histogram in H-S polar coordinates", HSPolar);

	waitKey();
	destroyAllWindows();

	return 0;
}

Mat getCbCrHist(const Mat& src, const int *channels) {
	Mat hist;
	int cr_bins = 512, cb_bins = 512;
	int histSize[] = { cr_bins, cb_bins };
	float cr_range[] = { 0, 256 };
	float cb_range[] = { 0, 256 };
	const float* ranges[] = { cr_range, cb_range };
	calcHist(&src, 1, channels, noArray(), hist, 2, histSize, ranges);

	return hist;
}

Mat getHSVHist(const Mat& src, const int* channels) {
	Mat hist;
	int x_bins = 256, y_bins = 180;
	int histSize[] = { x_bins, y_bins };
	float x_range[] = { 0, 256 };
	float y_range[] = { 0, 180 };
	const float* ranges[] = { x_range, y_range };
	calcHist(&src, 1, channels, noArray(), hist, 2, histSize, ranges);

	return hist;
}

void cvtCartesian2PolarCV_32F(const cv::Mat& src, cv::Mat& dst) {
	int max = std::max(src.rows, src.cols);
	dst = cv::Mat::zeros(max * 2, max * 2, src.type());

	for (int h = 0; h < src.rows; ++h) {
		for (int s = 0; s < src.cols; ++s) {
			float theta = (s / (float)src.cols) * 2.f * CV_PI;

			int x = static_cast<int>(max + h * std::cos(theta));
			int y = static_cast<int>(max + h * std::sin(theta) * -1); // opencv에서는 y축이 반전되어 있으므로

			dst.at<float>(y, x) = src.at<float>(h, s);
		}
	}
}