Change Counter

The change counter is an in-class exercise we had to do for Spatial Media, where we had to write an application which counts change in a given image. We didn’t finish on time and it’s been on my mind, so I decided to have a go at it. Here is the openFrameworks code for the finished app. The sample images can be found here.

testApp.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#ifndef _TEST_APP
#define _TEST_APP
 
#define WHITE_THRESHOLD 244
#define AREA_THESHOLD 50
#define COLOR_THESHOLD 10
 
#include "ofMain.h"
 
class coin {
    public:
        void    load(string imgName);
 
        int     area;
        int     color;
};
 
class change {
    public:
        void    load(string imgName);
        void    seedFill(int x, int y, int tag);
        int     count(coin penny, coin nickel, coin dime, coin quarter);
 
        unsigned char* colorPixels;
        unsigned char* grayPixels;
        unsigned char* tagPixels;
 
        ofImage img;
        int     currTag;
};
 
class testApp : public ofBaseApp {
    public:
        void    setup();
 
        coin    penny;
        coin    nickel;
        coin    dime;
        coin    quarter;
 
        change  inMyPocket;
};
 
#endif

testApp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "testApp.h"
 
//--------------------------------------------------------------
void coin::load(string imgName) {
    // load the image
    ofImage img;
    img.loadImage(imgName);
    unsigned char* pixels = img.getPixels();
 
    // calculate the area
    area = 0;
    color = 0;
    int numPixels = img.width * img.height;
    for (int i=0; i < numPixels; i++) {
        int pixelAvg = pixels[3*i+2];
        if (pixelAvg < WHITE_THRESHOLD) {
            area++;
            color += pixelAvg;
        }
    }
    color /= area;
 
    printf("[area: %i  color: %i]\n", area, color);
}
 
//--------------------------------------------------------------
void change::load(string imgName) {
    // load the image
    img.loadImage(imgName);
    colorPixels = img.getPixels();
 
    // init the grayscale and the tags
    grayPixels = new unsigned char[img.width*img.height];
    tagPixels = new unsigned char[img.width*img.height];
    for (int i=0; i < img.width * img.height; i++) {
        int pixelAvg = (colorPixels[3*i] + colorPixels[3*i+1] + colorPixels[3*i+2]) / 3;
        grayPixels[i] = pixelAvg;            
        tagPixels[i] = 0;
    }
 
    // find the blobs
    printf("Coin Detection\n");
    currTag = 0;
    for (int i=1; i < img.width-1; i++) {
        for (int j=1; j < img.height-1; j++) {
            if (grayPixels[j*img.width + i] < WHITE_THRESHOLD && tagPixels[j*img.width + i] == 0) {
                currTag++;
                printf("> Found a coin with color %i at (%i, %i)!\n", grayPixels[j*img.width + i], i, j);
                seedFill(i, j, currTag);                
            }
        }
    }
}
 
//--------------------------------------------------------------
void change::seedFill(int x, int y, int tag) {
    if (grayPixels[y*img.width + x] < WHITE_THRESHOLD) {
        tagPixels[y*img.width + x] = tag;
 
        if (y > 0 && tagPixels[(y-1)*img.width + x] == 0)             // north
            seedFill(x, y-1, tag);  
        if (y < img.height-1 && tagPixels[(y+1)*img.width + x] == 0)  // south
            seedFill(x, y+1, tag);
        if (x < img.width-1 && tagPixels[y*img.width + (x+1)] == 0)   // east
            seedFill(x+1, y, tag);
        if (x > 0 && tagPixels[y*img.width + (x-1)] == 0)             // west 
            seedFill(x-1, y, tag);
    }
}
 
//--------------------------------------------------------------
int change::count(coin penny, coin nickel, coin dime, coin quarter) {
    // identify the coins
    int** coinAreas = new int*[currTag];
    for (int i=0; i < currTag; i++) {
        coinAreas[i] = new int[2];
        coinAreas[i][0] = 0;
        coinAreas[i][1] = 0;
    }
    for (int i=0; i < img.width * img.height; i++) {
        if (tagPixels[i] != 0) {
            coinAreas[tagPixels[i]-1][0]++;
            int pixelAvg = colorPixels[3*i+2];
            coinAreas[tagPixels[i]-1][1] += pixelAvg;
        }
    }    
    for (int i=0; i < currTag; i++) {
        coinAreas[i][1] /= coinAreas[i][0];
    }
 
    // count the value of all the change
    printf("Coin Identification\n");
    int value = 0;
    for (int i=0; i < currTag; i++) {
        if (ABS(coinAreas[i][0] - quarter.area) < AREA_THESHOLD) {
            printf("> Found a quarter");
            value += 25;
        }
        else if (ABS(coinAreas[i][0] - dime.area) < AREA_THESHOLD) {
            printf("> Found a dime");
            value += 10;
        }
        else if (ABS(coinAreas[i][1] - penny.color) < COLOR_THESHOLD) {
            printf("> Found a penny");
            value += 1;
        } 
        else {
            printf("> Found a nickel");
            value += 5;
        }
        printf(" [%i - %i]\n", coinAreas[i][0], coinAreas[i][1]);
    }
 
    printf("Found %i coins worth %i cents!\n", currTag, value); 
 
    return value;
}
 
//--------------------------------------------------------------
void testApp::setup() {
    printf("Coin Reference\n");
    printf("> Penny: ");
    penny.load("image1.jpg");
    printf("> Nickel: ");
    nickel.load("image2.jpg");
    printf("> Dime: ");
    dime.load("image3.jpg");
    printf("> Quarter: ");
    quarter.load("image4.jpg");
 
    printf("---------------------------\n");
    inMyPocket.load("image5.jpg");
    inMyPocket.count(penny, nickel, dime, quarter);
 
    printf("---------------------------\n");
    inMyPocket.load("image6.jpg");
    inMyPocket.count(penny, nickel, dime, quarter);
 
    printf("---------------------------\n");
    inMyPocket.load("image7.jpg");
    inMyPocket.count(penny, nickel, dime, quarter);
 
    printf("---------------------------\n");
    inMyPocket.load("image8.jpg");
    inMyPocket.count(penny, nickel, dime, quarter);
 
    std::exit(0);
}

0 Responses to “Change Counter”


  • No Comments

Leave a Reply