diff --git a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/CXX.includecache b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/CXX.includecache index 49e0ac8d23ca6c3988b74935376072cbbbae6381..bb2a916458b90e7a5d622425aab8edc689bc9e8c 100644 --- a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/CXX.includecache +++ b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/CXX.includecache @@ -30,6 +30,16 @@ opencv2/aruco/charuco.hpp opencv2/calib3d.hpp - +/home/thomas/Documents/Minimisation/apps/specular_estimation/src/Macbeth.h +stdio.h +- +opencv/cv.h +- +opencv/highgui.h +- +opencv2/calib3d/calib3d.hpp +- + /home/thomas/Documents/Minimisation/apps/specular_estimation/src/OpenCV.h opencv2/opencv.hpp - @@ -175,6 +185,8 @@ OpenGL.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/OpenGL.h Ceres.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/Ceres.h +Macbeth.h +/home/thomas/Documents/Minimisation/apps/specular_estimation/src/Macbeth.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/texture.cpp stdio.h @@ -1726,10 +1738,28 @@ ceres/internal/reenable_warnings.h /usr/local/include/ceres/version.h +/usr/local/include/opencv/cv.h +opencv2/core/core_c.h +/usr/local/include/opencv/opencv2/core/core_c.h +opencv2/imgproc/imgproc_c.h +/usr/local/include/opencv/opencv2/imgproc/imgproc_c.h +opencv2/photo/photo_c.h +/usr/local/include/opencv/opencv2/photo/photo_c.h +opencv2/video/tracking_c.h +/usr/local/include/opencv/opencv2/video/tracking_c.h +opencv2/objdetect/objdetect_c.h +/usr/local/include/opencv/opencv2/objdetect/objdetect_c.h + /usr/local/include/opencv/cxcore.h opencv2/core/core_c.h /usr/local/include/opencv/opencv2/core/core_c.h +/usr/local/include/opencv/highgui.h +opencv2/core/core_c.h +/usr/local/include/opencv/opencv2/core/core_c.h +opencv2/highgui/highgui_c.h +/usr/local/include/opencv/opencv2/highgui/highgui_c.h + /usr/local/include/opencv2/aruco.hpp opencv2/core.hpp - @@ -1760,6 +1790,10 @@ opencv2/core/affine.hpp opencv2/calib3d/calib3d_c.h /usr/local/include/opencv2/opencv2/calib3d/calib3d_c.h +/usr/local/include/opencv2/calib3d/calib3d.hpp +opencv2/calib3d.hpp +/usr/local/include/opencv2/calib3d/opencv2/calib3d.hpp + /usr/local/include/opencv2/calib3d/calib3d_c.h opencv2/core/core_c.h /usr/local/include/opencv2/calib3d/opencv2/core/core_c.h diff --git a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.internal b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.internal index b7a1fba24400a549e257513c931aec4a1ec544db..450084ec7c1319b635a68007a30b83ec396e10dc 100644 --- a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.internal +++ b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.internal @@ -10,6 +10,7 @@ apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/shader.cpp.o apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o /home/thomas/Documents/Minimisation/apps/specular_estimation/src/Ceres.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/Charuco.h + /home/thomas/Documents/Minimisation/apps/specular_estimation/src/Macbeth.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/OpenCV.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/OpenGL.h /home/thomas/Documents/Minimisation/apps/specular_estimation/src/objloader.h @@ -271,11 +272,14 @@ apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimat /usr/local/include/ceres/solver.h /usr/local/include/ceres/types.h /usr/local/include/ceres/version.h + /usr/local/include/opencv/cv.h /usr/local/include/opencv/cxcore.h + /usr/local/include/opencv/highgui.h /usr/local/include/opencv2/aruco.hpp /usr/local/include/opencv2/aruco/charuco.hpp /usr/local/include/opencv2/aruco/dictionary.hpp /usr/local/include/opencv2/calib3d.hpp + /usr/local/include/opencv2/calib3d/calib3d.hpp /usr/local/include/opencv2/calib3d/calib3d_c.h /usr/local/include/opencv2/core.hpp /usr/local/include/opencv2/core/affine.hpp diff --git a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.make b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.make index 7ea700576081f9ff069fdc9db06f3765ae4ce359..d5d6ff3b92d4a5ffad53528354716b1f5b536957 100644 --- a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.make +++ b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/depend.make @@ -9,6 +9,7 @@ apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/shader.cpp.o: ap apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: apps/specular_estimation/src/Ceres.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: apps/specular_estimation/src/Charuco.h +apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: apps/specular_estimation/src/Macbeth.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: apps/specular_estimation/src/OpenCV.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: apps/specular_estimation/src/OpenGL.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: apps/specular_estimation/src/objloader.h @@ -270,11 +271,14 @@ apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimat apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/ceres/solver.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/ceres/types.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/ceres/version.h +apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv/cv.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv/cxcore.h +apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv/highgui.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/aruco.hpp apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/aruco/charuco.hpp apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/aruco/dictionary.hpp apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/calib3d.hpp +apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/calib3d/calib3d.hpp apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/calib3d/calib3d_c.h apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/core.hpp apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o: /usr/local/include/opencv2/core/affine.hpp diff --git a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o index 3273a97207e4569bb6e22b3d76378e7bb4294a07..18508dc3b6318979144f5530d7859577e9466e71 100644 Binary files a/apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o and b/apps/specular_estimation/CMakeFiles/specular_estimation.dir/src/specular_estimation.cc.o differ diff --git a/apps/specular_estimation/src/Macbeth.h b/apps/specular_estimation/src/Macbeth.h new file mode 100644 index 0000000000000000000000000000000000000000..23463ff7a90fed6cb94af44dcc982dbb061043f8 --- /dev/null +++ b/apps/specular_estimation/src/Macbeth.h @@ -0,0 +1,671 @@ +#pragma once +#ifndef MACBETH_H +#define MACBETH_H + +#include <stdio.h> +#include <opencv/cv.h> +#include <opencv/highgui.h> +#include <opencv2/calib3d/calib3d.hpp> + +#define MACBETH_WIDTH 6 +#define MACBETH_HEIGHT 4 +#define MACBETH_SQUARES MACBETH_WIDTH * MACBETH_HEIGHT + +#define MAX_CONTOUR_APPROX 7 + +#define MAX_RGB_DISTANCE 444 + +// BabelColor averages in sRGB: +// http://www.babelcolor.com/main_level/ColorChecker.htm +// (converted to BGR order for comparison) +CvScalar colorchecker_srgb[MACBETH_HEIGHT][MACBETH_WIDTH] = { + { + cvScalar(67,81,115), + cvScalar(129,149,196), + cvScalar(157,123,93), + cvScalar(65,108,90), + cvScalar(176,129,130), + cvScalar(171,191,99) + }, + { + cvScalar(45,123,220), + cvScalar(168,92,72), + cvScalar(98,84,195), + cvScalar(105,59,91), + cvScalar(62,189,160), + cvScalar(41,161,229) + }, + { + cvScalar(147,62,43), + cvScalar(72,149,71), + cvScalar(56,48,176), + cvScalar(22,200,238), + cvScalar(150,84,188), + cvScalar(166,136,0) + }, + { + cvScalar(240,245,245), + cvScalar(201,201,200), + cvScalar(161,161,160), + cvScalar(121,121,120), + cvScalar(85,84,83), + cvScalar(50,50,50) + } +}; + + struct ColorChecker { + double error; + CvMat * values; + CvMat * points; + double size; +}; + +cv::Mat detectMacbeth(std::string imageName); +double euclidean_distance(CvScalar p_1, CvScalar p_2); +double euclidean_distance(CvPoint p_1, CvPoint p_2); +double euclidean_distance_lab(CvScalar p_1, CvScalar p_2); +CvRect contained_rectangle(CvBox2D box); +CvScalar rect_average(CvRect rect, IplImage* image); +CvScalar contour_average(CvContour* contour, IplImage* image); +void rotate_box(CvPoint2D32f * box_corners); +double check_colorchecker(CvMat * colorchecker); +void draw_colorchecker(CvMat * colorchecker_values, CvMat * colorchecker_points, IplImage * image, int size); +ColorChecker find_colorchecker(CvSeq * quads, CvSeq * boxes, CvMemStorage *storage, IplImage *image, IplImage *original_image); +CvSeq * find_quad( CvSeq * src_contour, CvMemStorage *storage, int min_size); +IplImage * find_macbeth(const char *img); + +cv::Mat detectMacbeth(std::string imageName) { + //if( argc < 2 ) + //{ + //fprintf( stderr, "Usage: %s image_file [output_image]\n", argv[0] ); + //return 1; + //} + + const char *img_file = imageName.c_str(); + + IplImage *out = find_macbeth(img_file); + + //if( argc == 3) { + //cvSaveImage( argv[2], out ); + //} + + cv::Mat macbeth = cv::cvarrToMat(out); + + cvReleaseImage( &out ); + + //cv::Mat macbeth cv::cvarrToMat(find_macbeth(img_file)); + + return macbeth; +} + +double euclidean_distance(CvScalar p_1, CvScalar p_2) { + double sum = 0; + for(int i = 0; i < 3; i++) { + sum += std::pow(p_1.val[i]-p_2.val[i],2.); + } + return std::sqrt(sum); +} + +double euclidean_distance(CvPoint p_1, CvPoint p_2) { + return euclidean_distance(cvScalar(p_1.x,p_1.y,0),cvScalar(p_2.x,p_2.y,0)); +} + +double euclidean_distance_lab(CvScalar p_1, CvScalar p_2) { + // convert to Lab for better perceptual distance + IplImage * convert = cvCreateImage( cvSize(2,1), 8, 3); + cvSet2D(convert,0,0,p_1); + cvSet2D(convert,0,1,p_2); + cvCvtColor(convert,convert,CV_BGR2Lab); + p_1 = cvGet2D(convert,0,0); + p_2 = cvGet2D(convert,0,1); + cvReleaseImage(&convert); + + return euclidean_distance(p_1, p_2); +} + +CvRect contained_rectangle(CvBox2D box) { + return cvRect(box.center.x - box.size.width/4, + box.center.y - box.size.height/4, + box.size.width/2, + box.size.height/2); +} + +CvScalar rect_average(CvRect rect, IplImage* image) { + CvScalar average = cvScalarAll(0); + int count = 0; + for(int x = rect.x; x < (rect.x+rect.width); x++) { + for(int y = rect.y; y < (rect.y+rect.height); y++) { + if((x >= 0) && (y >= 0) && (x < image->width) && (y < image->height)) { + CvScalar s = cvGet2D(image,y,x); + average.val[0] += s.val[0]; + average.val[1] += s.val[1]; + average.val[2] += s.val[2]; + + count++; + } + } + } + + for(int i = 0; i < 3; i++) { + average.val[i] /= count; + } + + return average; +} + +CvScalar contour_average(CvContour* contour, IplImage* image) { + CvRect rect = ((CvContour*)contour)->rect; + + CvScalar average = cvScalarAll(0); + int count = 0; + for(int x = rect.x; x < (rect.x+rect.width); x++) { + for(int y = rect.y; y < (rect.y+rect.height); y++) { + if(cvPointPolygonTest(contour, cvPointTo32f(cvPoint(x,y)),0) == 100) { + CvScalar s = cvGet2D(image,y,x); + average.val[0] += s.val[0]; + average.val[1] += s.val[1]; + average.val[2] += s.val[2]; + + count++; + } + } + } + + for(int i = 0; i < 3; i++) { + average.val[i] /= count; + } + + return average; +} + +void rotate_box(CvPoint2D32f * box_corners) { + CvPoint2D32f last = box_corners[3]; + for(int i = 3; i > 0; i--) { + box_corners[i] = box_corners[i-1]; + } + box_corners[0] = last; +} + +double check_colorchecker(CvMat * colorchecker) { + double difference = 0; + + for(int x = 0; x < MACBETH_WIDTH; x++) { + for(int y = 0; y < MACBETH_HEIGHT; y++) { + CvScalar known_value = colorchecker_srgb[y][x]; + CvScalar test_value = cvGet2D(colorchecker,y,x); + for(int i = 0; i < 3; i++){ + difference += std::pow(known_value.val[i]-test_value.val[i],2); + } + } + } + + return difference; +} + +void draw_colorchecker(CvMat * colorchecker_values, CvMat * colorchecker_points, IplImage * image, int size) { + for(int x = 0; x < MACBETH_WIDTH; x++) { + for(int y = 0; y < MACBETH_HEIGHT; y++) { + CvScalar this_color = cvGet2D(colorchecker_values,y,x); + CvScalar this_point = cvGet2D(colorchecker_points,y,x); + + cvCircle( + image, + cvPoint(this_point.val[0],this_point.val[1]), + size, + colorchecker_srgb[y][x], + -1 + ); + + cvCircle( + image, + cvPoint(this_point.val[0],this_point.val[1]), + size/2, + this_color, + -1 + ); + } + } +} + +ColorChecker find_colorchecker(CvSeq * quads, CvSeq * boxes, CvMemStorage *storage, IplImage *image, IplImage *original_image) { + CvPoint2D32f box_corners[4]; + bool passport_box_flipped = false; + bool rotated_box = false; + + CvMat* points = cvCreateMat( boxes->total , 1, CV_32FC2 ); + for(int i = 0; i < boxes->total; i++) + { + CvBox2D box = (*(CvBox2D*)cvGetSeqElem(boxes, i)); + cvSet1D(points, i, cvScalar(box.center.x,box.center.y)); + } + CvBox2D passport_box = cvMinAreaRect2(points,storage); + fprintf(stderr,"Box:\n\tCenter: %f,%f\n\tSize: %f,%f\n\tAngle: %f\n",passport_box.center.x,passport_box.center.y,passport_box.size.width,passport_box.size.height,passport_box.angle); + if(passport_box.angle < 0.0) { + passport_box_flipped = true; + } + + cvBoxPoints(passport_box, box_corners); + // for(int i = 0; i < 4; i++) + // { + // fprintf(stderr,"Box corner %d: %d,%d\n",i,cvPointFrom32f(box_corners[i]).x,cvPointFrom32f(box_corners[i]).y); + // } + + // cvBox(passport_box, image, cvScalarAll(128), 10); + + if(euclidean_distance(cvPointFrom32f(box_corners[0]),cvPointFrom32f(box_corners[1])) < + euclidean_distance(cvPointFrom32f(box_corners[1]),cvPointFrom32f(box_corners[2]))) { + fprintf(stderr,"Box is upright, rotating\n"); + rotate_box(box_corners); + rotated_box = true && passport_box_flipped; + } + + double horizontal_spacing = euclidean_distance( + cvPointFrom32f(box_corners[0]),cvPointFrom32f(box_corners[1]))/(double)(MACBETH_WIDTH-1); + double vertical_spacing = euclidean_distance( + cvPointFrom32f(box_corners[1]),cvPointFrom32f(box_corners[2]))/(double)(MACBETH_HEIGHT-1); + double horizontal_slope = (box_corners[1].y - box_corners[0].y)/(box_corners[1].x - box_corners[0].x); + double horizontal_mag = std::sqrt(1+std::pow(horizontal_slope,2)); + double vertical_slope = (box_corners[3].y - box_corners[0].y)/(box_corners[3].x - box_corners[0].x); + double vertical_mag = std::sqrt(1+std::pow(vertical_slope,2)); + double horizontal_orientation = box_corners[0].x < box_corners[1].x ? -1 : 1; + double vertical_orientation = box_corners[0].y < box_corners[3].y ? -1 : 1; + + fprintf(stderr,"Spacing is %f %f\n",horizontal_spacing,vertical_spacing); + fprintf(stderr,"Slope is %f %f\n", horizontal_slope,vertical_slope); + + int average_size = 0; + for(int i = 0; i < boxes->total; i++) + { + CvBox2D box = (*(CvBox2D*)cvGetSeqElem(boxes, i)); + + CvRect rect = contained_rectangle(box); + average_size += MIN(rect.width, rect.height); + } + average_size /= boxes->total; + + fprintf(stderr,"Average contained rect size is %d\n", average_size); + + CvMat * this_colorchecker = cvCreateMat(MACBETH_HEIGHT, MACBETH_WIDTH, CV_32FC3); + CvMat * this_colorchecker_points = cvCreateMat( MACBETH_HEIGHT, MACBETH_WIDTH, CV_32FC2 ); + + // calculate the averages for our oriented colorchecker + for(int x = 0; x < MACBETH_WIDTH; x++) { + for(int y = 0; y < MACBETH_HEIGHT; y++) { + CvPoint2D32f row_start; + + if ( ((image->origin == IPL_ORIGIN_BL) || !rotated_box) && !((image->origin == IPL_ORIGIN_BL) && rotated_box) ) + { + row_start.x = box_corners[0].x + vertical_spacing * y * (1 / vertical_mag); + row_start.y = box_corners[0].y + vertical_spacing * y * (vertical_slope / vertical_mag); + } + else + { + row_start.x = box_corners[0].x - vertical_spacing * y * (1 / vertical_mag); + row_start.y = box_corners[0].y - vertical_spacing * y * (vertical_slope / vertical_mag); + } + + CvRect rect = cvRect(0,0,average_size,average_size); + + rect.x = row_start.x - horizontal_spacing * x * ( 1 / horizontal_mag ) * horizontal_orientation; + rect.y = row_start.y - horizontal_spacing * x * ( horizontal_slope / horizontal_mag ) * vertical_orientation; + + cvSet2D(this_colorchecker_points, y, x, cvScalar(rect.x,rect.y)); + + rect.x = rect.x - average_size / 2; + rect.y = rect.y - average_size / 2; + + // cvRectangle( + // image, + // cvPoint(rect.x,rect.y), + // cvPoint(rect.x+rect.width, rect.y+rect.height), + // cvScalarAll(0), + // 10 + // ); + + CvScalar average_color = rect_average(rect, original_image); + + cvSet2D(this_colorchecker,y,x,average_color); + } + } + + double orient_1_error = check_colorchecker(this_colorchecker); + cvFlip(this_colorchecker,NULL,-1); + double orient_2_error = check_colorchecker(this_colorchecker); + + fprintf(stderr,"Orientation 1: %f\n",orient_1_error); + fprintf(stderr,"Orientation 2: %f\n",orient_2_error); + + if(orient_1_error < orient_2_error) { + cvFlip(this_colorchecker,NULL,-1); + } + else { + cvFlip(this_colorchecker_points,NULL,-1); + } + + // draw_colorchecker(this_colorchecker,this_colorchecker_points,image,average_size); + + ColorChecker found_colorchecker; + + found_colorchecker.error = MIN(orient_1_error,orient_2_error); + found_colorchecker.values = this_colorchecker; + found_colorchecker.points = this_colorchecker_points; + found_colorchecker.size = average_size; + + return found_colorchecker; +} + +CvSeq * find_quad( CvSeq * src_contour, CvMemStorage *storage, int min_size) { + // stolen from icvGenerateQuads + CvMemStorage * temp_storage = cvCreateChildMemStorage( storage ); + + int flags = CV_CALIB_CB_FILTER_QUADS; + CvSeq *dst_contour = 0; + + const int min_approx_level = 2, max_approx_level = MAX_CONTOUR_APPROX; + int approx_level; + for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) + { + dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage, + CV_POLY_APPROX_DP, (float)approx_level ); + // we call this again on its own output, because sometimes + // cvApproxPoly() does not simplify as much as it should. + dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage, + CV_POLY_APPROX_DP, (float)approx_level ); + + if( dst_contour->total == 4 ) + break; + } + + // reject non-quadrangles + if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) ) + { + CvPoint pt[4]; + double d1, d2, p = cvContourPerimeter(dst_contour); + double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ)); + double dx, dy; + + for( int i = 0; i < 4; i++ ) + pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i); + + dx = pt[0].x - pt[2].x; + dy = pt[0].y - pt[2].y; + d1 = std::sqrt(dx*dx + dy*dy); + + dx = pt[1].x - pt[3].x; + dy = pt[1].y - pt[3].y; + d2 = std::sqrt(dx*dx + dy*dy); + + // philipg. Only accept those quadrangles which are more square + // than rectangular and which are big enough + double d3, d4; + dx = pt[0].x - pt[1].x; + dy = pt[0].y - pt[1].y; + d3 = std::sqrt(dx*dx + dy*dy); + dx = pt[1].x - pt[2].x; + dy = pt[1].y - pt[2].y; + d4 = std::sqrt(dx*dx + dy*dy); + if( !(flags & CV_CALIB_CB_FILTER_QUADS) || + (d3*1.1 > d4 && d4*1.1 > d3 && d3*d4 < area*1.5 && area > min_size && + d1 >= 0.15 * p && d2 >= 0.15 * p) ) + { + // CvContourEx* parent = (CvContourEx*)(src_contour->v_prev); + // parent->counter++; + // if( !board || board->counter < parent->counter ) + // board = parent; + // dst_contour->v_prev = (CvSeq*)parent; + //for( i = 0; i < 4; i++ ) cvLine( debug_img, pt[i], pt[(i+1)&3], cvScalar(200,255,255), 1, CV_AA, 0 ); + // cvSeqPush( root, &dst_contour ); + return dst_contour; + } + } + + return NULL; +} + +IplImage * find_macbeth(const char *img) { + IplImage * macbeth_img = cvLoadImage( img, + CV_LOAD_IMAGE_ANYCOLOR|CV_LOAD_IMAGE_ANYDEPTH ); + + IplImage * macbeth_original = cvCreateImage( cvSize(macbeth_img->width, macbeth_img->height), macbeth_img->depth, macbeth_img->nChannels ); + cvCopy(macbeth_img, macbeth_original); + + IplImage * macbeth_split[3]; + IplImage * macbeth_split_thresh[3]; + + for(int i = 0; i < 3; i++) { + macbeth_split[i] = cvCreateImage( cvSize(macbeth_img->width, macbeth_img->height), macbeth_img->depth, 1 ); + macbeth_split_thresh[i] = cvCreateImage( cvSize(macbeth_img->width, macbeth_img->height), macbeth_img->depth, 1 ); + } + + cvSplit(macbeth_img, macbeth_split[0], macbeth_split[1], macbeth_split[2], NULL); + + if( macbeth_img ) + { + int adaptive_method = CV_ADAPTIVE_THRESH_MEAN_C; + int threshold_type = CV_THRESH_BINARY_INV; + int block_size = cvRound( + MIN(macbeth_img->width,macbeth_img->height)*0.02)|1; + fprintf(stderr,"Using %d as block size\n", block_size); + + double offset = 6; + + // do an adaptive threshold on each channel + for(int i = 0; i < 3; i++) { + cvAdaptiveThreshold(macbeth_split[i], macbeth_split_thresh[i], 255, adaptive_method, threshold_type, block_size, offset); + } + + IplImage * adaptive = cvCreateImage( cvSize(macbeth_img->width, macbeth_img->height), IPL_DEPTH_8U, 1 ); + + // OR the binary threshold results together + cvOr(macbeth_split_thresh[0],macbeth_split_thresh[1],adaptive); + cvOr(macbeth_split_thresh[2],adaptive,adaptive); + + for(int i = 0; i < 3; i++) { + cvReleaseImage( &(macbeth_split[i]) ); + cvReleaseImage( &(macbeth_split_thresh[i]) ); + } + + int element_size = (block_size/10)+2; + fprintf(stderr,"Using %d as element size\n", element_size); + + // do an opening on the threshold image + IplConvKernel * element = cvCreateStructuringElementEx(element_size,element_size,element_size/2,element_size/2,CV_SHAPE_RECT); + cvMorphologyEx(adaptive,adaptive,NULL,element,CV_MOP_OPEN); + cvReleaseStructuringElement(&element); + + CvMemStorage* storage = cvCreateMemStorage(0); + + CvSeq* initial_quads = cvCreateSeq( 0, sizeof(*initial_quads), sizeof(void*), storage ); + CvSeq* initial_boxes = cvCreateSeq( 0, sizeof(*initial_boxes), sizeof(CvBox2D), storage ); + + // find contours in the threshold image + CvSeq * contours = NULL; + cvFindContours(adaptive,storage,&contours); + + int min_size = (macbeth_img->width*macbeth_img->height)/ + (MACBETH_SQUARES*100); + + if(contours) { + int count = 0; + + for( CvSeq* c = contours; c != NULL; c = c->h_next) { + CvRect rect = ((CvContour*)c)->rect; + // only interested in contours with these restrictions + if(CV_IS_SEQ_HOLE(c) && rect.width*rect.height >= min_size) { + // only interested in quad-like contours + CvSeq * quad_contour = find_quad(c, storage, min_size); + if(quad_contour) { + cvSeqPush( initial_quads, &quad_contour ); + count++; + rect = ((CvContour*)quad_contour)->rect; + + CvScalar average = contour_average((CvContour*)quad_contour, macbeth_img); + + CvBox2D box = cvMinAreaRect2(quad_contour,storage); + cvSeqPush( initial_boxes, &box ); + + // fprintf(stderr,"Center: %f %f\n", box.center.x, box.center.y); + + double min_distance = MAX_RGB_DISTANCE; + CvPoint closest_color_idx = cvPoint(-1,-1); + for(int y = 0; y < MACBETH_HEIGHT; y++) { + for(int x = 0; x < MACBETH_WIDTH; x++) { + double distance = euclidean_distance_lab(average,colorchecker_srgb[y][x]); + if(distance < min_distance) { + closest_color_idx.x = x; + closest_color_idx.y = y; + min_distance = distance; + } + } + } + + CvScalar closest_color = colorchecker_srgb[closest_color_idx.y][closest_color_idx.x]; + // fprintf(stderr,"Closest color: %f %f %f (%d %d)\n", + // closest_color.val[2], + // closest_color.val[1], + // closest_color.val[0], + // closest_color_idx.x, + // closest_color_idx.y + // ); + + // cvDrawContours( + // macbeth_img, + // quad_contour, + // cvScalar(255,0,0), + // cvScalar(0,0,255), + // 0, + // element_size + // ); + // cvCircle( + // macbeth_img, + // cvPointFrom32f(box.center), + // element_size*6, + // cvScalarAll(255), + // -1 + // ); + // cvCircle( + // macbeth_img, + // cvPointFrom32f(box.center), + // element_size*6, + // closest_color, + // -1 + // ); + // cvCircle( + // macbeth_img, + // cvPointFrom32f(box.center), + // element_size*4, + // average, + // -1 + // ); + // CvRect rect = contained_rectangle(box); + // cvRectangle( + // macbeth_img, + // cvPoint(rect.x,rect.y), + // cvPoint(rect.x+rect.width, rect.y+rect.height), + // cvScalarAll(0), + // element_size + // ); + } + } + } + + ColorChecker found_colorchecker; + + fprintf(stderr,"%d initial quads found", initial_quads->total); + if(count > MACBETH_SQUARES) { + fprintf(stderr," (probably a Passport)\n"); + + CvMat* points = cvCreateMat( initial_quads->total , 1, CV_32FC2 ); + CvMat* clusters = cvCreateMat( initial_quads->total , 1, CV_32SC1 ); + + CvSeq* partitioned_quads[2]; + CvSeq* partitioned_boxes[2]; + for(int i = 0; i < 2; i++) { + partitioned_quads[i] = cvCreateSeq( 0, sizeof(**partitioned_quads), sizeof(void*), storage ); + partitioned_boxes[i] = cvCreateSeq( 0, sizeof(**partitioned_boxes), sizeof(CvBox2D), storage ); + } + + // set up the points sequence for cvKMeans2, using the box centers + for(int i = 0; i < initial_quads->total; i++) { + CvBox2D box = (*(CvBox2D*)cvGetSeqElem(initial_boxes, i)); + + cvSet1D(points, i, cvScalar(box.center.x,box.center.y)); + } + + // partition into two clusters: passport and colorchecker + cvKMeans2( points, 2, clusters, + cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, + 10, 1.0 ) ); + + for(int i = 0; i < initial_quads->total; i++) { + CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i]; + int cluster_idx = clusters->data.i[i]; + + cvSeqPush( partitioned_quads[cluster_idx], + cvGetSeqElem(initial_quads, i) ); + cvSeqPush( partitioned_boxes[cluster_idx], + cvGetSeqElem(initial_boxes, i) ); + + // cvCircle( + // macbeth_img, + // cvPointFrom32f(pt), + // element_size*2, + // cvScalar(255*cluster_idx,0,255-(255*cluster_idx)), + // -1 + // ); + } + + ColorChecker partitioned_checkers[2]; + + // check each of the two partitioned sets for the best colorchecker + for(int i = 0; i < 2; i++) { + partitioned_checkers[i] = + find_colorchecker(partitioned_quads[i], partitioned_boxes[i], + storage, macbeth_img, macbeth_original); + } + + // use the colorchecker with the lowest error + found_colorchecker = partitioned_checkers[0].error < partitioned_checkers[1].error ? + partitioned_checkers[0] : partitioned_checkers[1]; + + cvReleaseMat( &points ); + cvReleaseMat( &clusters ); + } + else { // just one colorchecker to test + fprintf(stderr,"\n"); + found_colorchecker = find_colorchecker(initial_quads, initial_boxes, + storage, macbeth_img, macbeth_original); + } + + // render the found colorchecker + draw_colorchecker(found_colorchecker.values,found_colorchecker.points,macbeth_img,found_colorchecker.size); + + // print out the colorchecker info + for(int y = 0; y < MACBETH_HEIGHT; y++) { + for(int x = 0; x < MACBETH_WIDTH; x++) { + CvScalar this_value = cvGet2D(found_colorchecker.values,y,x); + CvScalar this_point = cvGet2D(found_colorchecker.points,y,x); + + printf("%.0f,%.0f,%.0f,%.0f,%.0f\n", + this_point.val[0],this_point.val[1], + this_value.val[2],this_value.val[1],this_value.val[0]); + } + } + printf("%0.f\n%f\n",found_colorchecker.size,found_colorchecker.error); + + } + + cvReleaseMemStorage( &storage ); + + if( macbeth_original ) cvReleaseImage( &macbeth_original ); + if( adaptive ) cvReleaseImage( &adaptive ); + + return macbeth_img; + } + + if( macbeth_img ) cvReleaseImage( &macbeth_img ); + + return NULL; +} + +#endif diff --git a/apps/specular_estimation/src/OpenCV.h b/apps/specular_estimation/src/OpenCV.h index db32c9f82163825f8c15d9e670b1825880fe36fc..c34fa9095f31dfc50d17775c9df13512d6e065f5 100644 --- a/apps/specular_estimation/src/OpenCV.h +++ b/apps/specular_estimation/src/OpenCV.h @@ -515,7 +515,7 @@ void loadCalibrationImages(std::vector< cv::Mat >& calibrationImages, std::strin std::cout << std::endl; } -void loadModelImages(std::vector< cv::Mat >& modelImages, std::vector< cv::Mat >& textureImages, std::string modelPath, int imageScale, int& originalWidth, int& originalHeight, int& width, int& height) { +void loadModelImages(std::vector<cv::Mat>& modelImages, std::vector<cv::Mat>& textureImages, std::string modelPath, int imageScale, int& originalWidth, int& originalHeight, int& width, int& height) { bool loadedImages = false; int imageNumber = 0; diff --git a/apps/specular_estimation/src/specular_estimation.cc b/apps/specular_estimation/src/specular_estimation.cc index 7eaf008a0bf4edd9a8822dda2849d2b9b041aec8..22a6d7d06195e15b99491cf2f266e49e14d566c1 100644 --- a/apps/specular_estimation/src/specular_estimation.cc +++ b/apps/specular_estimation/src/specular_estimation.cc @@ -53,6 +53,8 @@ int main(int argc, char** argv) { const std::string calibrationPath = imagesPath + folderPath + "/" + calibration + "/" + calibration + "."; const std::string texturePath = modelPath + "texture.png"; + detectMacbeth("/media/thomas/ESD-USB/2017-12-04/macbeth/macbeth.0.png"); + // A vector of Mats allow multiple images to be allocated to the same object // textureImages are the full colour photographs that are used to create the texture // modelImages are the greyscale version of textureImages which are used to calculate the surface normals diff --git a/apps/specular_estimation/src/stdafx.h b/apps/specular_estimation/src/stdafx.h index 6fb1e3b2951814d5813da11c262efc09ae9cc04f..cc35b5476994dbbc2711830d34b97729ccfd4261 100644 --- a/apps/specular_estimation/src/stdafx.h +++ b/apps/specular_estimation/src/stdafx.h @@ -42,3 +42,4 @@ using namespace glm; #include "Charuco.h" #include "OpenGL.h" #include "Ceres.h" +#include "Macbeth.h" diff --git a/bin/specular_estimation b/bin/specular_estimation index 8776e9adcc6672f9e56738805e253acf44e835b3..0e8da448870bd7cf723e3efcb59428ad5610ee5d 100755 Binary files a/bin/specular_estimation and b/bin/specular_estimation differ