| 133 | | /* in in HSV, out in BGR :) */ |
| 134 | | px_t mark_type(const IplImage *in, IplImage *out, px_params_t px_params, |
| 135 | | get_type_cb_t get_type_cb) |
| 136 | | { |
| 137 | | int x, y; |
| 138 | | //uint32_t type_avg_h = 0, type_avg_s = 0, type_avg_v = 0, type_count = 1; |
| 139 | | for (x = 0; x < in->width; x++) { |
| 140 | | for (y = 0; y < in->height; y++) { |
| 141 | | px_t px = get_type_cb(in, out, x, y, px_params); |
| 142 | | if (IS_PX(px, PX_IGNORE)) continue; |
| 143 | | SET_PX(out, x, y, px); |
| 144 | | |
| 145 | | //const px_t rpx = GET_PX(in, x, y); |
| 146 | | //fprintf(stderr, "%d %d %d\n", rpx.hsv.h, rpx.hsv.s, |
| 147 | | // rpx.hsv.v); |
| 148 | | //if (IS_PX(px, px_params.target)) { |
| 149 | | // type_avg_h += rpx.hsv.h; |
| 150 | | // type_avg_s += rpx.hsv.s; |
| 151 | | // type_avg_v += rpx.hsv.v; |
| 152 | | // type_count++; |
| 153 | | //} |
| 154 | | } |
| 155 | | } |
| 156 | | //printf("h = %lld, s = %lld, v = %lld, count = %lld\n", type_avg_h, |
| 157 | | // type_avg_s, type_avg_v, type_count); |
| 158 | | //type_avg_h /= type_count; |
| 159 | | //type_avg_s /= type_count; |
| 160 | | //type_avg_v /= type_count; |
| 161 | | //px_t type_avg = {.hsv = { |
| 162 | | // .h = type_avg_h, |
| 163 | | // .s = type_avg_s, |
| 164 | | // .v = type_avg_v |
| 165 | | //}}; |
| 166 | | px_t type_avg = {{0,0,0}}; |
| 167 | | return type_avg; |
| 168 | | } |
| 169 | | |
| 170 | | /** |
| 171 | | * Generalize this, e.g. |
| 172 | | * if (pass > 2) { grass_stuff; ..; } |
| 173 | | * Specify a number of iterations certain things need |
| 174 | | * Save values inbetween in static/global variables |
| 175 | | */ |
| 176 | | inline px_t get_grass(const IplImage *in, IplImage *out, int x, int y, px_params_t px_params) |
| 177 | | { |
| 178 | | const px_t px = GET_PX(in, x, y); |
| 179 | | if (y < in->height*0.10) |
| 180 | | return PX_SKY; |
| 181 | | else if (px.hsv.h > px_params.hue_min && px.hsv.h < px_params.hue_max && |
| 182 | | px.hsv.s > px_params.sat_min && px.hsv.s < px_params.sat_max && |
| 183 | | px.hsv.v > px_params.val_min && px.hsv.v < px_params.val_max ) |
| 184 | | return px_params.target; |
| 185 | | else |
| 186 | | return PX_OBSTICLE; |
| 187 | | } |
| 188 | | void mark_grass(const IplImage *in, IplImage *out) |
| 189 | | { |
| 190 | | /* Default grass colors */ |
| 191 | | px_params_t px_params = { |
| 192 | | /* Rose images */ |
| 193 | | //.hue_min = 10, .hue_max = 50, // basic green |
| 194 | | //.sat_min = 40, .sat_max = 255, // no upper bound |
| 195 | | //.val_min = 20, .val_max = 255, // no default upper bound |
| 196 | | .hue_min = 30, .hue_max = 120, // Any Hue |
| 197 | | .sat_min = 0, .sat_max = 90, // no upper bound |
| 198 | | .val_min = 80, .val_max = 180, // no default upper bound |
| 199 | | .target = PX_GRASS |
| 200 | | }; |
| 201 | | px_t grass_avg = mark_type(in, out, px_params, get_grass); |
| 202 | | printf("hsv.h = %02x, hsv.s = %02x, hsv.v = %02x\n", grass_avg.hsv.h, grass_avg.hsv.s, grass_avg.hsv.v); |
| 203 | | //px_params.hue_min = MAX(grass_avg.hsv.h - 15, 0); |
| 204 | | //px_params.hue_max = MIN(grass_avg.hsv.h + 13, 256); |
| 205 | | //px_params.sat_min = MAX(grass_avg.hsv.s - 25, 0); |
| 206 | | //px_params.sat_max = MIN(grass_avg.hsv.s + 30, 256); |
| 207 | | //px_params.val_min = MAX(grass_avg.hsv.v - 20, 0); |
| 208 | | //px_params.val_max = MIN(grass_avg.hsv.v + 20, 256); |
| 209 | | //mark_type(in, out, width, height, px_params, get_grass); |
| 210 | | } |
| 211 | | |
| 212 | | /* can only one at a time (white or yellow) :( */ |
| 213 | | inline px_t get_lines(const IplImage *in, IplImage *out, int x, int y, px_params_t px_params) |
| 214 | | { |
| 215 | | if (y < 30) |
| 216 | | return PX_IGNORE; |
| 217 | | const px_t px = GET_PX(in, x, y); |
| 218 | | if (px.hsv.h > px_params.hue_min && px.hsv.h < px_params.hue_max && |
| 219 | | px.hsv.s > px_params.sat_min && px.hsv.s < px_params.sat_max && |
| 220 | | px.hsv.v > px_params.val_min && px.hsv.v < px_params.val_max ) { |
| 221 | | //return px_params.target; |
| 222 | | int rad = in->width*0.10; |
| 223 | | /* Check if there's grass (or nothing) on both sides */ |
| 224 | | if (((x-rad < 0 || IS_PX(GET_PX(out, x-rad, y), PX_GRASS)) && |
| 225 | | (x+rad >= in->width || IS_PX(GET_PX(out, x+rad, y), PX_GRASS))) || |
| 226 | | ((y-rad < 0 || IS_PX(GET_PX(out, x, y-rad), PX_GRASS)) && |
| 227 | | (y+rad >= in->height || IS_PX(GET_PX(out, x, y+rad), PX_GRASS)))) { |
| 228 | | return px_params.target; |
| 229 | | } |
| 230 | | } |
| 231 | | return PX_IGNORE; |
| 232 | | } |
| 233 | | void mark_lines(const IplImage *in, IplImage *out) |
| 234 | | { |
| 235 | | /* Default grass colors */ |
| 236 | | px_params_t px_params = { |
| 237 | | //.hue_min = 0, .hue_max = 180, // Any Hue |
| 238 | | //.sat_min = 30, .sat_max = 70, // no upper bound |
| 239 | | //.val_min = 60, .val_max = 120, // no default upper bound |
| 240 | | .hue_min = 100, .hue_max = 120, // basic green (good) |
| 241 | | .sat_min = 60, .sat_max = 90, // no upper bound |
| 242 | | .val_min = 140, .val_max = 254, // no default upper bound |
| 243 | | .target = PX_LINE |
| 244 | | }; |
| 245 | | px_t white_avg = mark_type(in, out, px_params, get_lines); |
| 246 | | printf("hsv.h = %d, hsv.s = %d, hsv.v = %d\n", |
| 247 | | white_avg.hsv.h, white_avg.hsv.s, white_avg.hsv.v); |
| 248 | | } |
| 249 | | void expand_lines(const IplImage *out1, IplImage *out2) |
| 250 | | { |
| 251 | | CvPoint pt; |
| 252 | | int rad = out1->width*0.003; |
| 253 | | int size = sizeof(px_t)*(rad*2+1); |
| 254 | | for (pt.x = 0; pt.x < out1->width; pt.x++) { |
| 255 | | for (pt.y = 0; pt.y < out1->height; pt.y++) { |
| 256 | | if (pt.x-rad < 0 || pt.x+rad >= out2->width || |
| 257 | | pt.y-rad < 0 || pt.y+rad >= out2->height) |
| 258 | | continue; // Skip out of bounds |
| 259 | | if (IS_PX(GET_PX(out1, pt.x, pt.y), PX_LINE)) { |
| 260 | | //int x,y; |
| 261 | | //for (x=-rad, y= rad; x <= rad; x++) SET_PX(out2, pt.x+x, pt.y+y, PX_LINE); |
| 262 | | //for (x=-rad, y=-rad; x <= rad; x++) SET_PX(out2, pt.x+x, pt.y+y, PX_LINE); |
| 263 | | //for (y=-rad, x= rad; y <= rad; y++) SET_PX(out2, pt.x+x, pt.y+y, PX_LINE); |
| 264 | | //for (y=-rad, x=-rad; y <= rad; y++) SET_PX(out2, pt.x+x, pt.y+y, PX_LINE); |
| 265 | | px_t *top = (px_t*)(out2->imageData + out2->widthStep*(pt.y+rad) + out2->nChannels*pt.x); |
| 266 | | px_t *mid = (px_t*)(out2->imageData + out2->widthStep*(pt.y ) + out2->nChannels*pt.x); |
| 267 | | px_t *bot = (px_t*)(out2->imageData + out2->widthStep*(pt.y-rad) + out2->nChannels*pt.x); |
| 268 | | memset(top, -1, size); |
| 269 | | memset(mid, -1, size); |
| 270 | | memset(bot, -1, size); |
| 271 | | //for (int y = pt.y-rad; y < pt.y+rad; y++) { |
| 272 | | // px_t *yln = (px_t*)(out2->imageData + out2->widthStep*(pt.y+rad) + out2->nChannels*pt.x); |
| 273 | | // memset(yln, -1, size); |
| 274 | | //} |
| 275 | | //cvCircle(out2, pt, 10, color, 1, 0, 0); |
| 276 | | //cvCircle(out2, pt, 10, color, 1, 0, 0); |
| 277 | | } |
| 278 | | //if (x-rad < 0 || x+rad >= out1->width || |
| 279 | | // y-rad < 0 || y+rad >= out1->height) |
| 280 | | // continue; // Skip out of bounds |
| 281 | | //if (IS_PX(GET_PX(out1, x-rad, y), PX_LINE) || |
| 282 | | // IS_PX(GET_PX(out1, x+rad, y), PX_LINE) || |
| 283 | | // IS_PX(GET_PX(out1, x, y-rad), PX_LINE) || |
| 284 | | // IS_PX(GET_PX(out1, x, y+rad), PX_LINE) || |
| 285 | | // IS_PX(GET_PX(out1, x, y ), PX_LINE)) { |
| 286 | | // SET_PX(out2, x, y, PX_LINE); |
| 287 | | //} |
| 288 | | } |
| 289 | | } |
| 290 | | } |