Home | History | Annotate | Download | only in colorchecker
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #define LOG_NDEBUG 0
     17 
     18 #define LOG_TAG "ColorCheckerTest"
     19 #include <utils/Log.h>
     20 #include <utils/Timers.h>
     21 #include <cmath>
     22 #include <string>
     23 
     24 #include "vec2.h"
     25 #include "vec3.h"
     26 #include "colorcheckertest.h"
     27 
     28 const float GAMMA_CORRECTION = 2.2f;
     29 const float COLOR_ERROR_THRESHOLD = 200.f;
     30 ColorCheckerTest::~ColorCheckerTest() {
     31     ALOGV("Deleting color checker test handler");
     32 
     33     if (mImage != NULL) {
     34         delete mImage;
     35     }
     36     ALOGV("Image deleted");
     37 
     38     int numHorizontalLines = mCandidateColors.size();
     39     int numVerticalLines = mCandidateColors[0].size();
     40 
     41     for (int i = 0; i < numHorizontalLines; ++i) {
     42         for (int j = 0; j < numVerticalLines; ++j) {
     43             if (mCandidateColors[i][j] != NULL) {
     44                 delete mCandidateColors[i][j];
     45             }
     46             if (mCandidatePositions[i][j] != NULL) {
     47                 delete mCandidatePositions[i][j];
     48             }
     49         }
     50     }
     51     ALOGV("Candidates deleted!");
     52 
     53     for (int i = 0; i < 4; ++i) {
     54         for (int j = 0; j < 6; ++j) {
     55             if (mMatchPositions[i][j] != NULL) {
     56                 delete mMatchPositions[i][j];
     57             }
     58             if (mReferenceColors[i][j] != NULL) {
     59                 delete mReferenceColors[i][j];
     60             }
     61             if (mMatchColors[i][j] != NULL) {
     62                 delete mMatchColors[i][j];
     63             }
     64         }
     65     }
     66 }
     67 
     68 // Adds a new image to the test handler.
     69 void ColorCheckerTest::addTestingImage(TestingImage* inputImage) {
     70     if (mImage != NULL) {
     71         delete mImage;
     72     }
     73     mImage = NULL;
     74     ALOGV("Original image deleted");
     75     mImage = inputImage;
     76 
     77     if ((mImage->getHeight() == getDebugHeight()) &&
     78         (mImage->getWidth() == getDebugWidth())) {
     79         copyDebugImage(getDebugHeight(), getDebugWidth(), mImage->getImage());
     80     }
     81 }
     82 
     83 void ColorCheckerTest::processData() {
     84     mSuccess = false;
     85     initializeRefColor();
     86     edgeDetection();
     87 }
     88 
     89 void ColorCheckerTest::initializeRefColor() {
     90     mReferenceColors.resize(4, std::vector<Vec3i*>(6, NULL));
     91     mMatchPositions.resize(4, std::vector<Vec2f*>(6, NULL));
     92     mMatchColors.resize(4, std::vector<Vec3f*>(6, NULL));
     93     mMatchRadius.resize(4, std::vector<float>(6, 0.f));
     94 
     95     mReferenceColors[0][0]= new Vec3i(115, 82, 68);
     96     mReferenceColors[0][1]= new Vec3i(194, 150, 130);
     97     mReferenceColors[0][2]= new Vec3i(98, 122, 157);
     98     mReferenceColors[0][3]= new Vec3i(87, 108, 67);
     99     mReferenceColors[0][4]= new Vec3i(133, 128, 177);
    100     mReferenceColors[0][5]= new Vec3i(103, 189, 170);
    101     mReferenceColors[1][0]= new Vec3i(214, 126, 44);
    102     mReferenceColors[1][1]= new Vec3i(80, 91, 166);
    103     mReferenceColors[1][2]= new Vec3i(193, 90, 99);
    104     mReferenceColors[1][3]= new Vec3i(94,  60, 108);
    105     mReferenceColors[1][4]= new Vec3i(157, 188, 64);
    106     mReferenceColors[1][5]= new Vec3i(224, 163, 46);
    107     mReferenceColors[2][0]= new Vec3i(56, 61, 150);
    108     mReferenceColors[2][1]= new Vec3i(70, 148, 73);
    109     mReferenceColors[2][2]= new Vec3i(175, 54, 60);
    110     mReferenceColors[2][3]= new Vec3i(231, 199, 31);
    111     mReferenceColors[2][4]= new Vec3i(187, 86, 149);
    112     mReferenceColors[2][5]= new Vec3i(8, 133, 161);
    113     mReferenceColors[3][0]= new Vec3i(243, 243, 242);
    114     mReferenceColors[3][1]= new Vec3i(200, 200, 200);
    115     mReferenceColors[3][2]= new Vec3i(160, 160, 160);
    116     mReferenceColors[3][3]= new Vec3i(122, 122, 121);
    117     mReferenceColors[3][4]= new Vec3i(85, 85, 85);
    118     mReferenceColors[3][5]= new Vec3i(52, 52, 52);
    119 }
    120 
    121 void ColorCheckerTest::edgeDetection() {
    122     int width = mImage->getWidth();
    123     int height = mImage->getHeight();
    124 
    125     bool* edgeMap = new bool[height * width];
    126     unsigned char* grayImage = new unsigned char[height * width];
    127 
    128     // If the image is a color image and can be converted to a luminance layer
    129     if (mImage->rgbToGrayScale(grayImage)) {
    130         float* gradientMap = new float[height * width * 2];
    131 
    132         // Computes the gradient image on the luminance layer.
    133         computeGradient(grayImage, gradientMap);
    134 
    135         float* gradientMagnitude = new float[height * width];
    136         int* gradientDirectionInt = new int[height * width];
    137         float* gradientDirection = new float[height * width];
    138 
    139         // Computes the absolute gradient of the image without padding.
    140         for (int i = 1; i < height - 1; ++i) {
    141             for (int j = 1; j < width - 1; ++j) {
    142                 gradientMagnitude[i * width + j] =
    143                         sqrt(gradientMap[(i * width + j) * 2] *
    144                              gradientMap[(i * width + j) * 2] +
    145                              gradientMap[(i * width + j ) * 2 + 1] *
    146                              gradientMap[(i * width + j ) * 2 + 1]);
    147 
    148                 // Computes the gradient direction of the image.
    149                 if (gradientMap[(i * width + j) * 2] == 0 ) {
    150                     // If the vertical gradient is 0, the edge is horizontal
    151                     // Mark the gradient direction as 90 degrees.
    152                     gradientDirectionInt[i * width + j] = 2;
    153                     gradientDirection[i * width + j] = 90.0f;
    154                 } else {
    155                     // Otherwise the atan operation is valid and can decide
    156                     // the gradient direction of the edge.
    157                     float gradient = atan(gradientMap[(i * width + j) * 2 + 1]
    158                             / gradientMap[(i * width + j) * 2])
    159                             / (M_PI / 4);
    160 
    161                     gradientDirection[i * width + j] = gradient * 45.0f;
    162 
    163                     // Maps the gradient direction to 4 major directions with
    164                     // 0 mapped to up and 2 mapped to right.
    165                     if (gradient - floor(gradient) > 0.5) {
    166                         gradientDirectionInt[i * width + j] =
    167                                 (static_cast<int>(ceil(gradient)) + 4) % 4;
    168                     } else {
    169                         gradientDirectionInt[i * width + j] =
    170                                 (static_cast<int>(floor(gradient)) + 4) % 4;
    171                     }
    172                 }
    173             }
    174         }
    175 
    176         // Compute a boolean map to show whether a pixel is on the edge.
    177         for (int i = 1; i < height - 1; ++i) {
    178             for (int j = 1; j < width - 1; ++j) {
    179                 edgeMap[i * width + j] = false;
    180 
    181                 switch (gradientDirectionInt[i * width + j]) {
    182                     case 0:
    183                         // If the gradient points rightwards, the pixel is
    184                         // on an edge if it has a larger absolute gradient than
    185                         // pixels on its left and right sides.
    186                         if ((gradientMagnitude[i * width + j] >=
    187                                 gradientMagnitude[i * width + j + 1]) &&
    188                             (gradientMagnitude[i * width + j] >=
    189                                 gradientMagnitude[i * width + j - 1])) {
    190                             edgeMap[i * width + j] = true;
    191                         }
    192                         break;
    193                     case 1:
    194                         // If the gradient points right-downwards, the pixel is
    195                         // on an edge if it has a larger absolute gradient than
    196                         // pixels on its upper left and bottom right sides.
    197                         if ((gradientMagnitude[i * width + j] >=
    198                                 gradientMagnitude[(i + 1) * width + j + 1]) &&
    199                             (gradientMagnitude[i * width + j] >=
    200                                 gradientMagnitude[(i - 1) * width + j - 1])) {
    201                             edgeMap[i * width + j] = true;
    202                         }
    203                         break;
    204                     case 2:
    205                         // If the gradient points upwards, the pixel is
    206                         // on an edge if it has a larger absolute gradient than
    207                         // pixels on its up and down sides.
    208                         if ((gradientMagnitude[i * width + j] >=
    209                                 gradientMagnitude[(i + 1) * width + j]) &&
    210                             (gradientMagnitude[i * width + j] >=
    211                                 gradientMagnitude[(i - 1) * width + j])) {
    212                             edgeMap[i * width + j] = true;
    213                         }
    214                         break;
    215                     case 3:
    216                         // If the gradient points right-upwards, the pixel is
    217                         // on an edge if it has a larger absolute gradient than
    218                         // pixels on its bottom left and upper right sides.
    219                         if ((gradientMagnitude[i * width + j] >=
    220                                 gradientMagnitude[(i - 1) * width + j + 1]) &&
    221                             (gradientMagnitude[i * width + j] >=
    222                                 gradientMagnitude[(i + 1) * width + j - 1])) {
    223                             edgeMap[i * width + j] = true;
    224                         }
    225                   }
    226 
    227              }
    228         }
    229 
    230         houghLineDetection(edgeMap, gradientMagnitude, gradientDirection);
    231 
    232         // Cleans up
    233         delete[] gradientMap;
    234         delete[] gradientDirectionInt;
    235         delete[] gradientMagnitude;
    236         delete[] gradientDirection;
    237 
    238     } else {
    239         ALOGE("Not a color image!");
    240     }
    241 
    242     delete[] edgeMap;
    243     delete[] grayImage;
    244 }
    245 
    246 // Runs the hough voting algorithm to find the grid of the color checker
    247 // with the edge map, gradient direction and gradient magnitude as inputs.
    248 void ColorCheckerTest::houghLineDetection(bool* edgeMap,
    249                                           float* gradientMagnitude,
    250                                           float* gradientDirection) {
    251     // Constructs a graph for Hough voting. The vertical axis counts the vote
    252     // for a certain angle. The horizontal axis counts the vote for the distance
    253     // of a line from the origin of the image.
    254     int houghHeight = 180;
    255     int houghWidth = 200;
    256     int houghCounts[houghHeight][houghWidth];
    257     int houghSum[houghHeight][houghWidth];
    258 
    259     int** houghVote;
    260     houghVote = new int*[180];
    261     for (int i = 0; i < 180; ++i) {
    262         houghVote[i] = new int[200];
    263     }
    264 
    265     for (int i = 0; i < houghHeight; ++i) {
    266         for (int j = 0; j < houghWidth; ++j) {
    267             houghCounts[i][j] = 0;
    268             houghVote[i][j] = 0;
    269             houghSum[i][j] = 0;
    270         }
    271     }
    272 
    273     // Vectors to record lines in two orthogonal directions.
    274     // Each line is represented by its direction and its distance to the origin.
    275     std::vector<std::vector<int> > verticalLines;
    276     std::vector<std::vector<int> > horizontalLines;
    277     float radius;
    278     int height = mImage->getHeight();
    279     int width = mImage->getWidth();
    280 
    281     // Processes the signicant edge pixels and cast vote for the corresponding
    282     // edge passing this pixel.
    283     for (int i = 1; i < height - 1; ++i) {
    284         for (int j = 1; j < width - 1; ++j) {
    285             // Sets threashold for the gradient magnitude to discount noises
    286             if (edgeMap[i * width + j] &&
    287                 (gradientMagnitude[i * width + j] > 15)) {
    288                 int shiftedAngle;
    289 
    290                 // Shifts angles for 45 degrees so the vertical and horizontal
    291                 // direction is mapped to 45 and 135 degrees to avoid padding.
    292                 // This uses the assumption that the color checker is placed
    293                 // roughly parallel to the image boarders. So that the edges
    294                 // at the angle of 45 will be rare.
    295                 shiftedAngle = (static_cast<int>(
    296                         -gradientDirection[i * width + j]) + 225 % 180);
    297                 float shiftedAngleRad = static_cast<float>(shiftedAngle)
    298                         * M_PI / 180.0f;
    299 
    300                 // Computes the distance of the line from the origin.
    301                 float a, b;
    302                 a = static_cast<float>(i - j) / sqrt(2.0f);
    303                 b = static_cast<float>(i + j) / sqrt(2.0f);
    304                 radius = a * sin(shiftedAngleRad) - b * cos(shiftedAngleRad);
    305 
    306                 // Adds one vote for the line. The line's angle is shifted by
    307                 // 45 degrees to avoid avoid padding for the vertical lines,
    308                 // which is more common than diagonal lines. The line's
    309                 // distance is mapped to [0, 200] from [-200, 200].
    310                 ++houghCounts[shiftedAngle][static_cast<int>((radius / 2.0f) +
    311                                                               100.0f)];
    312 
    313                 drawPoint(i, j, Vec3i(255, 255, 255));
    314             }
    315         }
    316     }
    317 
    318     int houghAngleSum[houghHeight];
    319     int primaryVerticalAngle, primaryHorizontalAngle;
    320     int max1 = 0;
    321     int max2 = 0;
    322 
    323     // Looking for the two primary angles of the lines.
    324     for (int i = 5; i < houghHeight - 5; ++i) {
    325         houghAngleSum[i] = 0;
    326         for (int j = 0; j < houghWidth; ++j) {
    327             for (int l = -5; l <= 5; ++l) {
    328                 houghSum[i][j] += houghCounts[i + l][j];
    329             }
    330             houghAngleSum[i] += houghSum[i][j];
    331         }
    332 
    333         if ((i < houghHeight / 2) && (houghAngleSum[i] > max1)) {
    334             max1 = houghAngleSum[i];
    335             primaryVerticalAngle = i;
    336         } else if ((i > houghHeight / 2) && (houghAngleSum[i] > max2)) {
    337             max2 = houghAngleSum[i];
    338             primaryHorizontalAngle = i;
    339         }
    340     }
    341 
    342     ALOGV("Primary angles are %d, %d",
    343          primaryVerticalAngle, primaryHorizontalAngle);
    344 
    345     int angle;
    346 
    347     // For each primary angle, look for the highest voted lines.
    348     for (int k = 0; k < 2; ++k) {
    349         if (k == 0) {
    350             angle = primaryVerticalAngle;
    351         } else {
    352             angle = primaryHorizontalAngle;
    353         }
    354 
    355         std::vector<int> line(2);
    356         for (int j = 2; j < houghWidth - 2; ++j) {
    357             houghVote[angle][j] = houghSum[angle][j];
    358             houghSum[angle][j] = 0;
    359         }
    360 
    361         // For each radius, average the vote with nearby ones.
    362         for (int j = 2; j < houghWidth - 2; ++j) {
    363             for (int m = -2; m <= 2; ++m) {
    364                 houghSum[angle][j] += houghVote[angle][j + m];
    365             }
    366         }
    367 
    368         bool isCandidate[houghWidth];
    369 
    370         // Find whether a lines is a candidate by rejecting the ones that have
    371         // lower vote than others in the neighborhood.
    372         for (int j = 2; j < houghWidth - 2; ++j) {
    373             isCandidate[j] = true;
    374             for (int m = -2; ((isCandidate[j]) && (m <= 2)); ++m) {
    375                 if ((houghSum[angle][j] < 20) ||
    376                     (houghSum[angle][j] < houghSum[angle][j + m])) {
    377                     isCandidate[j] = false;
    378                 }
    379             }
    380         }
    381 
    382         int iter1 = 0;
    383         int iter2 = 0;
    384         int count = 0;
    385 
    386         // Finds the lines that are not too close to each other and add to the
    387         // detected lines.
    388         while (iter2 < houghWidth) {
    389             while ((!isCandidate[iter2]) && (iter2 < houghWidth)) {
    390                 ++iter2;
    391             }
    392             if ((isCandidate[iter2]) && (iter2 - iter1 < 5)) {
    393                 iter1 = (iter2 + iter1) / 2;
    394                 ++iter2;
    395             } else {
    396                 line[0] = angle;
    397                 line[1] = (iter1 - 100) * 2;
    398                 if (iter1 != 0) {
    399                     if (k == 0) {
    400                         verticalLines.push_back(line);
    401                         Vec3i color(verticalLines.size() * 20, 0, 0);
    402                         drawLine(line[0], line[1], color);
    403                     } else {
    404                         horizontalLines.push_back(line);
    405                         Vec3i color(0, horizontalLines.size() * 20, 0);
    406                         drawLine(line[0], line[1], color);
    407                     }
    408                 }
    409                 iter1 = iter2;
    410                 ++iter2;
    411                 ALOGV("pushing back line %d %d", line[0], line[1]);
    412             }
    413         }
    414     }
    415 
    416     ALOGV("Numbers of lines in each direction is %d, %d",
    417          verticalLines.size(), horizontalLines.size());
    418 
    419     for (int i = 0; i < 180; ++i) {
    420         delete[] houghVote[i];
    421     }
    422     delete[] houghVote;
    423 
    424     findCheckerBoards(verticalLines, horizontalLines);
    425 }
    426 
    427 // Computes the gradient in both x and y direction of a layer
    428 void ColorCheckerTest::computeGradient(unsigned char* layer,
    429                                        float* gradientMap) {
    430     int width = mImage->getWidth();
    431     int height = mImage->getHeight();
    432 
    433     // Computes the gradient in the whole image except the image boarders.
    434     for (int i = 1; i < height - 1; ++i) {
    435         for (int j = 1; j < width - 1; ++j) {
    436             gradientMap[(i * width + j) * 2] =
    437                     0.5f * (layer[i * width + j + 1] -
    438                             layer[i * width + j - 1]);
    439             gradientMap[(i * width + j) * 2 + 1] =
    440                     0.5f * (layer[(i + 1) * width + j] -
    441                            layer[(i - 1) * width + j]);
    442         }
    443     }
    444 }
    445 
    446 // Tries to find the checker boards with the highest voted lines
    447 void ColorCheckerTest::findCheckerBoards(
    448         std::vector<std::vector<int> > verticalLines,
    449         std::vector<std::vector<int> > horizontalLines) {
    450     ALOGV("Start looking for Color checker");
    451 
    452     int numHorizontalLines = mCandidateColors.size();
    453     int numVerticalLines;
    454     if (numHorizontalLines > 0) {
    455         numVerticalLines = mCandidateColors[0].size();
    456         for (int i = 0; i < numHorizontalLines; ++i) {
    457             for (int j = 0; j < numVerticalLines; ++j) {
    458                 if (mCandidateColors[i][j] != NULL) {
    459                     delete mCandidateColors[i][j];
    460                 }
    461                 if (mCandidatePositions[i][j] != NULL) {
    462                     delete mCandidatePositions[i][j];
    463                 }
    464             }
    465             mCandidateColors[i].clear();
    466             mCandidatePositions[i].clear();
    467         }
    468     }
    469     mCandidateColors.clear();
    470     mCandidatePositions.clear();
    471 
    472     ALOGV("Candidates deleted!");
    473 
    474     numVerticalLines = verticalLines.size();
    475     numHorizontalLines = horizontalLines.size();
    476     Vec2f pointUpperLeft;
    477     Vec2f pointBottomRight;
    478 
    479     mCandidateColors.resize(numHorizontalLines - 1);
    480     mCandidatePositions.resize(numHorizontalLines - 1);
    481 
    482     for (int i = numVerticalLines - 1; i >= 1; --i) {
    483         for (int j = 0; j < numHorizontalLines - 1; ++j) {
    484             // Finds the upper left and bottom right corner of each rectangle
    485             // formed by two neighboring highest voted lines.
    486             pointUpperLeft = findCrossing(verticalLines[i], horizontalLines[j]);
    487             pointBottomRight = findCrossing(verticalLines[i - 1],
    488                                             horizontalLines[j + 1]);
    489 
    490             Vec3i* color = new Vec3i();
    491             Vec2f* pointCenter = new Vec2f();
    492             // Verifies if they are separated by a reasonable distance.
    493             if (verifyPointPair(pointUpperLeft, pointBottomRight,
    494                                 pointCenter, color)) {
    495                 mCandidatePositions[j].push_back(pointCenter);
    496                 mCandidateColors[j].push_back(color);
    497                 ALOGV("Color at (%d, %d) is (%d, %d, %d)", j, i,color->r(), color->g(), color->b());
    498 
    499             } else {
    500                 mCandidatePositions[j].push_back(NULL);
    501                 mCandidateColors[j].push_back(NULL);
    502                 delete color;
    503                 delete pointCenter;
    504             }
    505         }
    506     }
    507 
    508     ALOGV("Candidates Number (%d, %d)", mCandidateColors.size(), mCandidateColors[0].size());
    509     // Verifies whether the current line candidates form a valid color checker.
    510     verifyColorGrid();
    511 }
    512 
    513 // Returns the corssing point of two lines given the lines.
    514 Vec2f ColorCheckerTest::findCrossing(std::vector<int> line1,
    515                                      std::vector<int> line2) {
    516     Vec2f crossingPoint;
    517     float r1 = static_cast<float>(line1[1]);
    518     float r2 = static_cast<float>(line2[1]);
    519     float ang1, ang2;
    520     float y1, y2;
    521 
    522     ang1 = static_cast<float>(line1[0]) / 180.0f * M_PI;
    523     ang2 = static_cast<float>(line2[0]) / 180.0f * M_PI;
    524 
    525     float x, y;
    526     x = (r1 * cos(ang2) - r2 * cos(ang1)) / sin(ang1 - ang2);
    527     y = (r1 * sin(ang2) - r2 * sin(ang1)) / sin(ang1 - ang2);
    528 
    529     crossingPoint.set((x + y) / sqrt(2.0), (y - x) / sqrt(2.0));
    530 
    531     //ALOGV("Crosspoint at (%f, %f)", crossingPoint.x(), crossingPoint.y());
    532     return crossingPoint;
    533 }
    534 
    535 // Verifies whether two opposite corners on a quadrilateral actually can be
    536 // the two corners of a color checker.
    537 bool ColorCheckerTest::verifyPointPair(Vec2f pointUpperLeft,
    538                                        Vec2f pointBottomRight,
    539                                        Vec2f* pointCenter,
    540                                        Vec3i* color) {
    541     bool success = true;
    542 
    543     /** 5 and 30 are the threshold tuned for resolution 640*480*/
    544     if ((pointUpperLeft.x() < 0) ||
    545         (pointUpperLeft.x() >= mImage->getHeight()) ||
    546         (pointUpperLeft.y() < 0) ||
    547         (pointUpperLeft.y() >= mImage->getWidth()) ||
    548         (pointBottomRight.x() < 0) ||
    549         (pointBottomRight.x() >= mImage->getHeight()) ||
    550         (pointBottomRight.y() < 0) ||
    551         (pointBottomRight.y() >= mImage->getWidth()) ||
    552         (abs(pointUpperLeft.x() - pointBottomRight.x()) <= 5) ||
    553         (abs(pointUpperLeft.y() - pointBottomRight.y()) <= 5) ||
    554         (abs(pointUpperLeft.x() - pointBottomRight.x()) >= 30) ||
    555         (abs(pointUpperLeft.y() - pointBottomRight.y()) >= 30)) {
    556 
    557         // If any of the quadrilateral corners are out of the image or if
    558         // the distance between them are too large or too big, the quadrilateral
    559         // could not be one of the checkers
    560         success = false;
    561     } else {
    562         // Find the checker center if the corners of the rectangle meet criteria
    563         pointCenter->set((pointUpperLeft.x() + pointBottomRight.x()) / 2.0f,
    564                        (pointUpperLeft.y() + pointBottomRight.y()) / 2.0f);
    565         color->set(mImage->getPixelValue(*pointCenter).r(),
    566                    mImage->getPixelValue(*pointCenter).g(),
    567                    mImage->getPixelValue(*pointCenter).b());
    568         ALOGV("Color at (%f, %f) is (%d, %d, %d)", pointCenter->x(), pointCenter->y(),color->r(), color->g(), color->b());
    569     }
    570     return success;
    571 }
    572 
    573 // Verifies the color checker centers and finds the match between the detected
    574 // color checker and the reference MacBeth color checker
    575 void ColorCheckerTest::verifyColorGrid() {
    576     ALOGV("Start looking for Color Grid");
    577     int numHorizontalLines = mCandidateColors.size();
    578     int numVerticalLines = mCandidateColors[0].size();
    579     bool success = false;
    580 
    581     // Computes the standard deviation of one row/column of the proposed color
    582     // checker. Discards the row/column if the std is below a threshold.
    583     for (int i = 0; i < numHorizontalLines; ++i) {
    584         Vec3f meanColor(0.f, 0.f, 0.f);
    585         int numNonZero = 0;
    586 
    587         for (int j = 0; j < numVerticalLines; ++j) {
    588             if (mCandidateColors[i][j] != NULL) {
    589                 ALOGV("candidate color (%d, %d) is (%d, %d, %d)", i, j, mCandidateColors[i][j]->r(), mCandidateColors[i][j]->g(), mCandidateColors[i][j]->b());
    590 
    591                 meanColor = meanColor + (*mCandidateColors[i][j]);
    592                 ++numNonZero;
    593             }
    594         }
    595         if (numNonZero > 0) {
    596             meanColor = meanColor / numNonZero;
    597         }
    598         ALOGV("Mean color for vertical direction computed!");
    599 
    600         float std = 0;
    601         for (int j = 0; j < numVerticalLines; ++j) {
    602             if (mCandidateColors[i][j] != NULL) {
    603                 std += mCandidateColors[i][j]->squareDistance<float>(meanColor);
    604             }
    605         }
    606         if (numNonZero > 0) {
    607             std = sqrt(std / (3 * numNonZero));
    608         }
    609         ALOGV("st. deviation for the %d dir1 is %d", i, static_cast<int>(std));
    610 
    611         if ((std <= 30) && (numNonZero > 1)) {
    612             for (int j = 0; j < numVerticalLines; ++j) {
    613                 if (mCandidateColors[i][j] != NULL) {
    614                     delete mCandidateColors[i][j];
    615                     mCandidateColors[i][j] = NULL;
    616                 }
    617             }
    618         }
    619     }
    620 
    621     // Discards the column/row of the color checker if the std is below a
    622     // threshold.
    623     for (int j = 0; j < numVerticalLines; ++j) {
    624         Vec3f meanColor(0.f, 0.f, 0.f);
    625         int numNonZero = 0;
    626 
    627         for (int i = 0; i < numHorizontalLines; ++i) {
    628             if (mCandidateColors[i][j] != NULL) {
    629                 meanColor = meanColor + (*mCandidateColors[i][j]);
    630                 ++numNonZero;
    631             }
    632         }
    633         if (numNonZero > 0) {
    634             meanColor = meanColor / numNonZero;
    635         }
    636 
    637         float std = 0;
    638         for (int i = 0; i < numHorizontalLines; ++i) {
    639             if (mCandidateColors[i][j] != NULL) {
    640                 std += mCandidateColors[i][j]->squareDistance<float>(meanColor);
    641             }
    642         }
    643         if (numNonZero > 0) {
    644             std = sqrt(std / (3 * numNonZero));
    645         }
    646 
    647         ALOGV("st. deviation for the %d dir2 is %d", j, static_cast<int>(std));
    648 
    649         if ((std <= 30) && (numNonZero > 1)) {
    650             for (int i = 0; i < numHorizontalLines; ++i) {
    651                 if (mCandidateColors[i][j] != NULL) {
    652                     delete mCandidateColors[i][j];
    653                     mCandidateColors[i][j] = NULL;
    654                 }
    655             }
    656         }
    657     }
    658 
    659     for (int i = 0; i < numHorizontalLines; ++i) {
    660         for (int j = 0; j < numVerticalLines; ++j) {
    661             if (mCandidateColors[i][j] != NULL) {
    662                 ALOGV("position (%d, %d) is at (%f, %f) with color (%d, %d, %d)",
    663                      i, j,
    664                      mCandidatePositions[i][j]->x(),
    665                      mCandidatePositions[i][j]->y(),
    666                      mCandidateColors[i][j]->r(),
    667                      mCandidateColors[i][j]->g(),
    668                      mCandidateColors[i][j]->b());
    669             } else {
    670                 ALOGV("position (%d, %d) is 0", i, j);
    671             }
    672         }
    673     }
    674 
    675     // Finds the match between the detected color checker and the reference
    676     // MacBeth color checker.
    677     int rowStart = 0;
    678     int rowEnd = 0;
    679 
    680     // Loops until all dectected color checker has been processed.
    681     while (!success) {
    682         int columnStart = 0;
    683         int columnEnd = 0;
    684         bool isRowStart = false;
    685         bool isRowEnd = true;
    686 
    687         // Finds the row start of the next block of detected color checkers.
    688         while ((!isRowStart) && (rowStart <  numHorizontalLines)) {
    689             for (int j = 0; j < numVerticalLines; ++j) {
    690                 if (mCandidateColors[rowStart][j] != NULL) {
    691                     isRowStart = true;
    692                 }
    693             }
    694             ++rowStart;
    695         }
    696         rowStart--;
    697         rowEnd = rowStart;
    698         ALOGV("rowStart is %d", rowStart);
    699 
    700         // Finds the row end of the next block of detected color checkers.
    701         while ((isRowEnd) && (rowEnd < numHorizontalLines)) {
    702             isRowEnd = false;
    703             for (int j = 0; j < numVerticalLines; ++j) {
    704                 if (mCandidateColors[rowEnd][j] != NULL) {
    705                     isRowEnd= true;
    706                 }
    707             }
    708             if (isRowEnd) {
    709                 ++rowEnd;
    710             }
    711         }
    712         if ((!isRowEnd) && isRowStart) {
    713             rowEnd--;
    714         }
    715         if ((isRowEnd) && (rowEnd == numHorizontalLines)) {
    716             rowEnd--;
    717             isRowEnd = false;
    718         }
    719         ALOGV("rowEnd is %d", rowEnd);
    720 
    721         // Matches color checkers between the start row and the end row.
    722         bool successVertical = false;
    723 
    724         while (!successVertical) {
    725             bool isColumnEnd = true;
    726             bool isColumnStart = false;
    727 
    728             // Finds the start column of the next block of color checker
    729             while ((!isColumnStart) && (columnStart < numVerticalLines)) {
    730                 if (mCandidateColors[rowStart][columnStart] != NULL) {
    731                     isColumnStart = true;
    732                 }
    733                 ++columnStart;
    734             }
    735             columnStart--;
    736             columnEnd = columnStart;
    737 
    738             // Finds the end column of the next block of color checker
    739             while ((isColumnEnd) && (columnEnd < numVerticalLines)) {
    740                 isColumnEnd = false;
    741                 if (mCandidateColors[rowStart][columnEnd] != NULL)
    742                     isColumnEnd = true;
    743                 if (isColumnEnd) {
    744                     ++columnEnd;
    745                 }
    746             }
    747 
    748             if ((!isColumnEnd) && isColumnStart) {
    749                 columnEnd--;
    750             }
    751             if ((isColumnEnd) && (columnEnd == numVerticalLines)) {
    752                 columnEnd--;
    753                 isColumnEnd = false;
    754             }
    755 
    756             // Finds the best match on the MacBeth reference color checker for
    757             // the continuous block of detected color checker
    758             if (isRowStart && (!isRowEnd) &&
    759                 isColumnStart && (!isColumnEnd)) {
    760                 findBestMatch(rowStart, rowEnd, columnStart, columnEnd);
    761             }
    762             ALOGV("FindBestMatch for %d, %d, %d, %d", rowStart,
    763                  rowEnd, columnStart, columnEnd);
    764 
    765             // If the column search finishes, go out of the loop
    766             if (columnEnd >= numVerticalLines - 1) {
    767                 successVertical = true;
    768             } else {
    769                 columnStart = columnEnd + 1;
    770             }
    771         }
    772         ALOGV("Continuing to search for direction 1");
    773 
    774         // If the row search finishes, go out of the loop
    775         if (rowEnd >= numHorizontalLines - 1) {
    776             success = true;
    777         } else {
    778             rowStart = rowEnd + 1;
    779         }
    780     }
    781 
    782     for (int i = 0; i < 4; ++i) {
    783         for (int j = 0; j < 6; ++j) {
    784             if (mMatchPositions[i][j] != NULL) {
    785                 ALOGV("Reference Match position for (%d, %d) is (%f, %f)", i, j,
    786                      mMatchPositions[i][j]->x(), mMatchPositions[i][j]->y());
    787             }
    788         }
    789     }
    790 
    791     fillRefColorGrid();
    792 }
    793 
    794 // Finds the best match on the MacBeth color checker for the continuous block of
    795 // detected color checkers bounded by row i1, row i2 and column j1 and column j2
    796 // Assumes that the subsample is less than 4*6.
    797 void ColorCheckerTest::findBestMatch(int i1, int i2, int j1, int j2) {
    798     int numHorizontalGrid = i2 - i1 + 1;
    799     int numVerticalGrid = j2 - j1 + 1;
    800 
    801     if (((numHorizontalGrid > 1) || (numVerticalGrid > 1)) &&
    802         (numHorizontalGrid <= 4) && (numVerticalGrid <= 6)) {
    803         ALOGV("i1, j2, j1, j2 is %d, %d, %d, %d", i1, i2, j1, j2);
    804         float minError;
    805         float error = 0.f;
    806         int horizontalMatch, verticalMatch;
    807 
    808         // Finds the match start point where the error is minimized.
    809         for (int i = 0; i < numHorizontalGrid; ++i) {
    810             for (int j = 0; j < numVerticalGrid; ++j) {
    811                 if (mCandidateColors[i1 + i][j1 + j] != NULL) {
    812                     error += mCandidateColors[i1 + i][j1 + j]->squareDistance<int>(
    813                             *mReferenceColors[i][j]);
    814                 }
    815             }
    816         }
    817         ALOGV("Error is %f", error);
    818         minError = error;
    819         horizontalMatch = 0;
    820         verticalMatch = 0;
    821 
    822         for (int i = 0; i <= 4 - numHorizontalGrid; ++i) {
    823             for (int j = 0; j <= 6 - numVerticalGrid; ++j) {
    824                 error = 0.f;
    825 
    826                 for (int id = 0; id < numHorizontalGrid; ++id) {
    827                     for (int jd = 0; jd < numVerticalGrid; ++jd) {
    828                         if (mCandidateColors[i1 + id][j1 + jd] != NULL) {
    829                             error += mCandidateColors[i1 + id][j1 + jd]->
    830                                     squareDistance<int>(
    831                                             *mReferenceColors[i + id][j + jd]);
    832                         }
    833                     }
    834                 }
    835 
    836                 if (error < minError) {
    837                     minError = error;
    838                     horizontalMatch = i;
    839                     verticalMatch = j;
    840                 }
    841                 ALOGV("Processed %d, %d and error is %f", i, j, error );
    842             }
    843         }
    844 
    845         for (int id = 0; id < numHorizontalGrid; ++id) {
    846             for (int jd = 0; jd < numVerticalGrid; ++jd) {
    847                 if (mCandidatePositions[i1 + id][j1 + jd] != NULL) {
    848                     mMatchPositions[horizontalMatch + id][verticalMatch + jd] =
    849                             new Vec2f(mCandidatePositions[i1 + id][j1 + jd]->x(),
    850                                       mCandidatePositions[i1 + id][j1 + jd]->y());
    851                 }
    852             }
    853         }
    854         ALOGV("Grid match starts at %d, %d", horizontalMatch, verticalMatch);
    855     }
    856 }
    857 
    858 // Finds the boundary of a color checker by its color similarity to the center.
    859 // Also predicts the location of unmatched checkers.
    860 void ColorCheckerTest::fillRefColorGrid() {
    861     int rowStart = 0;
    862     int columnStart = 0;
    863     bool foundStart = true;
    864 
    865     for (int i = 0; (i < 4) && foundStart; ++i) {
    866         for (int j = 0; (j < 6) && foundStart; ++j) {
    867             if (mMatchPositions[i][j] != NULL) {
    868                 rowStart = i;
    869                 columnStart = j;
    870                 foundStart = false;
    871             }
    872         }
    873     }
    874     ALOGV("First match found at (%d, %d)", rowStart, columnStart);
    875 
    876     float rowDistance, columnDistance;
    877     rowDistance = 0;
    878     columnDistance = 0;
    879     int numRowGrids = 0;
    880     int numColumnGrids = 0;
    881 
    882     for (int i = rowStart; i < 4; ++i) {
    883         for (int j = columnStart; j < 6; ++j) {
    884             if (mMatchPositions[i][j] != NULL) {
    885                 if (i > rowStart) {
    886                     ++numRowGrids;
    887                     rowDistance += (mMatchPositions[i][j]->x() -
    888                                 mMatchPositions[rowStart][columnStart]->x()) /
    889                                 static_cast<float>(i - rowStart);
    890                 }
    891                 if (j > columnStart) {
    892                     ++numColumnGrids;
    893                     columnDistance += (mMatchPositions[i][j]->y() -
    894                                 mMatchPositions[rowStart][columnStart]->y()) /
    895                                 static_cast<float>(j - columnStart);
    896                 }
    897             }
    898         }
    899     }
    900 
    901     if ((numRowGrids > 0) && (numColumnGrids > 0)) {
    902         rowDistance = rowDistance / numRowGrids;
    903         columnDistance = columnDistance / numColumnGrids;
    904         ALOGV("delta is %f, %f", rowDistance, columnDistance);
    905 
    906         for (int i = 0; i < 4; ++i) {
    907             for (int j = 0 ; j < 6; ++j) {
    908                 if (mMatchPositions[i][j] == NULL) {
    909                     mMatchPositions[i][j] = new Vec2f(
    910                             mMatchPositions[rowStart][columnStart]->x() +
    911                                     (i - rowStart) * rowDistance,
    912                             mMatchPositions[rowStart][columnStart]->y() +
    913                                     (j - columnStart) * columnDistance);
    914                 }
    915             }
    916         }
    917         for (int i = 0; i < 4; ++i) {
    918             for (int j = 0; j < 6; ++j) {
    919                 float radius = 0;
    920                 Vec3i color = mImage->getPixelValue(*mMatchPositions[i][j]);
    921                 Vec3f meanColor(0.f , 0.f, 0.f);
    922 
    923                 int numPixels = 0;
    924                 for (int ii  = static_cast<int>(mMatchPositions[i][j]->x() -
    925                                                 rowDistance/2);
    926                      ii <= static_cast<int>(mMatchPositions[i][j]->x() +
    927                                             rowDistance/2);
    928                      ++ii) {
    929                     for (int jj = static_cast<int>(mMatchPositions[i][j]->y() -
    930                                                    columnDistance/2);
    931                          jj <= static_cast<int>(mMatchPositions[i][j]->y() +
    932                                                 columnDistance/2);
    933                          ++jj) {
    934                         if ((ii >= 0) && (ii < mImage->getHeight()) &&
    935                             (jj >= 0) && (jj < mImage->getWidth())) {
    936                             Vec3i pixelColor = mImage->getPixelValue(ii,jj);
    937                             float error = color.squareDistance<int>(pixelColor);
    938 
    939                             if (error < COLOR_ERROR_THRESHOLD) {
    940                                 drawPoint(ii, jj, *mReferenceColors[i][j]);
    941                                 meanColor = meanColor + pixelColor;
    942                                 numPixels++;
    943                                 Vec2i pixelPosition(ii, jj);
    944 
    945                                 if (pixelPosition.squareDistance<float>(
    946                                         *mMatchPositions[i][j]) > radius) {
    947                                     radius = pixelPosition.squareDistance<float>(
    948                                             *mMatchPositions[i][j]);
    949                                 }
    950                             }
    951                         }
    952                     }
    953                 }
    954 
    955                 /** Computes the radius of the checker.
    956                  * The above computed radius is the squared distance to the
    957                  * furthest point with a matching color. To be conservative, we
    958                  * only consider an area with radius half of the above computed
    959                  * value. Since radius is computed as a squared root, the one
    960                  * that will be recorded is 1/4 of the above computed value.
    961                  */
    962                 mMatchRadius[i][j] = radius / 4.f;
    963                 mMatchColors[i][j] = new Vec3f(meanColor / numPixels);
    964 
    965                 ALOGV("Reference color at (%d, %d) is (%d, %d, %d)", i, j,
    966                      mReferenceColors[i][j]->r(),
    967                      mReferenceColors[i][j]->g(),
    968                      mReferenceColors[i][j]->b());
    969                 ALOGV("Average color at (%d, %d) is (%f, %f, %f)", i, j,
    970                      mMatchColors[i][j]->r(),
    971                      mMatchColors[i][j]->g(),
    972                      mMatchColors[i][j]->b());
    973                 ALOGV("Radius is %f", mMatchRadius[i][j]);
    974             }
    975         }
    976 
    977         mSuccess = true;
    978     }
    979 }
    980