summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralex <alex@ASUS>2018-11-08 02:52:43 +0100
committeralex <alex@ASUS>2018-11-08 02:52:43 +0100
commit8d335244c3fdf0c383351449f3737c919cd9abf6 (patch)
tree86b31e28acf1d47b89b017c05434732a65e7387b
parent5518a5ee75606566fc0f4d00878c881be66cab95 (diff)
Add proc_coins; Optimize local_maxima;
-rw-r--r--modules/image/src/img_alx.cpp30
-rw-r--r--modules/proc/inc/proc.h7
-rw-r--r--modules/proc/inc/proc.hpp7
-rw-r--r--modules/proc/src/proc.cpp463
-rw-r--r--modules/user/inc/user_iface.h1
-rw-r--r--modules/user/inc/user_iface.hpp1
-rw-r--r--modules/user/src/user_clui.c13
-rw-r--r--modules/user/src/user_tui.c16
8 files changed, 519 insertions, 19 deletions
diff --git a/modules/image/src/img_alx.cpp b/modules/image/src/img_alx.cpp
index 5df01b1..4ed61ba 100644
--- a/modules/image/src/img_alx.cpp
+++ b/modules/image/src/img_alx.cpp
@@ -51,27 +51,35 @@ static void img_alx_local_max (class cv::Mat *imgptr)
int j;
int k;
int l;
- bool local_max;
- int dist_min;
- int step_img;
+ bool local_max;
+ int dist_min;
+ class cv::Mat imgtmp;
+
+ /* pointer to a pixel (in imgptr) */
uint8_t *img_pix;
+ /* pointer to a pixel near img_pix (in imgptr) */
uint8_t *near_pix;
- uint8_t *tmp_pix;
- class cv::Mat imgtmp;
+ /* pointer to a pixel (same position as img_pix, but in imgtmp) */
+ uint8_t *tmp_pix;
+ /* Tmp image copy */
imgptr->copyTo(imgtmp);
- step_img = imgptr->step;
+ /* Minimum distance between local maxima */
dist_min = 16;
for (i = 1; i < imgptr->rows - 1; i++) {
for (j = 1; j < imgptr->cols - 1; j++) {
- img_pix = imgptr->data + i * step_img + j;
- tmp_pix = imgtmp.data + i * step_img + j;
+ img_pix = imgptr->data + i * imgptr->step + j;
+ tmp_pix = imgtmp.data + i * imgptr->step + j;
local_max = true;
- for (k = i - dist_min; k < i + dist_min+1; k++) {
- for (l = j - dist_min; l < j + dist_min+1; l++) {
- near_pix = imgptr->data + k * step_img + l;
+ if (!(*img_pix)) {
+ local_max = false;
+ }
+
+ for (k = i - dist_min; (k < i + dist_min+1) && local_max; k++) {
+ for (l = j - dist_min; (l < j + dist_min+1) && local_max; l++) {
+ near_pix = imgptr->data + k * imgptr->step + l;
if (j >= 0 && j < imgptr->rows) {
if (l >= 0 && l < imgptr->cols) {
if (*img_pix < *near_pix) {
diff --git a/modules/proc/inc/proc.h b/modules/proc/inc/proc.h
index 498f9e5..1bae0b9 100644
--- a/modules/proc/inc/proc.h
+++ b/modules/proc/inc/proc.h
@@ -23,6 +23,7 @@
PROC_MODE = 0x4000,
PROC_MODE_LABEL,
+ PROC_MODE_COINS,
PROC_MODE_RESISTOR
};
@@ -42,6 +43,12 @@
LABEL_NOK_PRICE
};
+ enum Proc_Coins {
+ COINS_OK,
+ COINS_NOK_COINS,
+ COINS_NOK_OVERLAP
+ };
+
enum Proc_Resistor {
RESISTOR_OK,
RESISTOR_NOK_RESISTOR,
diff --git a/modules/proc/inc/proc.hpp b/modules/proc/inc/proc.hpp
index f933c39..affd2f4 100644
--- a/modules/proc/inc/proc.hpp
+++ b/modules/proc/inc/proc.hpp
@@ -23,6 +23,7 @@
PROC_MODE = 0x4000,
PROC_MODE_LABEL,
+ PROC_MODE_COINS,
PROC_MODE_RESISTOR
};
@@ -42,6 +43,12 @@
LABEL_NOK_PRICE
};
+ enum Proc_Coins {
+ COINS_OK,
+ COINS_NOK_COINS,
+ COINS_NOK_OVERLAP
+ };
+
enum Proc_Resistor {
RESISTOR_OK,
RESISTOR_NOK_RESISTOR,
diff --git a/modules/proc/src/proc.cpp b/modules/proc/src/proc.cpp
index 5e840dd..2c16cd4 100644
--- a/modules/proc/src/proc.cpp
+++ b/modules/proc/src/proc.cpp
@@ -52,6 +52,9 @@
static int proc_label (void);
static void result_label (int status);
+static int proc_coins (void);
+static void result_coins (int status);
+
static int proc_resistor (void);
static void result_resistor (int status);
@@ -59,6 +62,8 @@ static void proc_save_mem (int n);
static void proc_load_mem (int n);
static void proc_save_ref (void);
+static void proc_local_max (void);
+
static void proc_pixel_value (int x, int y, unsigned char *val);
static void proc_ROI (int x, int y, int w, int h);
static void proc_and_2ref (void);
@@ -72,8 +77,9 @@ static void proc_erode_dilate (int size);
static void proc_smooth (int method, int ksize);
static void proc_rotate (class cv::RotatedRect *rect);
static void proc_adaptive_threshold (int method, int type, int ksize);
-static void proc_threshold (int type, int ksize);
static void proc_cvt_color (int method);
+static void proc_distance_transform (void);
+static void proc_threshold (int type, int ksize);
static void proc_contours (
std::vector <std::vector <class cv::Point_ <int>>> *contours,
class cv::Mat *hierarchy);
@@ -114,6 +120,9 @@ int proc_iface_single (int action)
case PROC_MODE_LABEL:
error = proc_label();
break;
+ case PROC_MODE_COINS:
+ error = proc_coins();
+ break;
case PROC_MODE_RESISTOR:
error = proc_resistor();
break;
@@ -150,10 +159,15 @@ void proc_iface_series (void)
num_len = 4;
snprintf(file_ext, 80, ".BMP");
break;
+ case PROC_MODE_COINS:
+ snprintf(file_basename, 80, "c");
+ num_len = 4;
+ snprintf(file_ext, 80, ".jpeg");
+ break;
case PROC_MODE_RESISTOR:
snprintf(file_basename, 80, "res");
num_len = 4;
- snprintf(file_ext, 80, ".BMP");
+ snprintf(file_ext, 80, ".jpeg");
break;
}
@@ -495,6 +509,429 @@ static void result_label (int status)
(user_iface_log.len)++;
}
+static int proc_coins (void)
+{
+ int status;
+
+ double times;
+ clock_t time_0;
+ clock_t time_1;
+
+ std::vector <std::vector <cv::Point_ <int>>> contours;
+ class cv::Mat hierarchy;
+ class cv::RotatedRect rect;
+ int x;
+ int y;
+ int w;
+ int h;
+
+ struct {
+ /* found? */
+ bool found;
+
+ /* position */
+ int x;
+ int y;
+ } blobs [5];
+
+ struct {
+ /* found? */
+ bool found;
+
+ /* blob num */
+ int blob;
+
+ /* position */
+ int x;
+ int y;
+
+ /* value */
+ unsigned char h;
+ unsigned char s;
+ unsigned char v;
+ } bands [5];
+
+ char code [6] = {'\0', '\0', '\0', '\0', '\0', '\0'};
+ float resistance;
+ int tolerance;
+
+ int i;
+ int j;
+
+ proc_save_mem(0);
+ /* Find coins */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ proc_cmp(IMG_IFACE_CMP_RED);
+ proc_threshold(cv::THRESH_BINARY, 10);
+ proc_smooth(IMGI_SMOOTH_MEDIAN, 9);
+ proc_distance_transform();
+ proc_local_max();
+ proc_dilate(8);
+ proc_save_mem(1);
+
+ proc_contours(&contours, &hierarchy);
+
+ /* If no contour is found, error: NOK_COINS */
+ if (!contours.size()) {
+ status = COINS_NOK_COINS;
+ result_coins(status);
+ return status;
+ }
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time0: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+ }
+#if 0 /* Align resistor and crop */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ proc_load_mem(0);
+ proc_rotate(&rect);
+ x = rect.center.x - (0.8 * rect.size.width / 2.0);
+ if (x < 0) {
+ x = 0;
+ }
+ y = rect.center.y - (0 * rect.size.height / 2.0);
+ if (y < 0) {
+ y = 0;
+ }
+ w = rect.size.width * 0.8;
+ h = rect.size.height * 0.3;
+ proc_ROI(x, y, w, h);
+ proc_save_mem(1);
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time1: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+ }
+ /* Separate background (BK) and lines (WH) */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ proc_smooth(IMGI_SMOOTH_MEDIAN, 5);
+ proc_cvt_color(cv::COLOR_BGR2HSV);
+ proc_smooth(IMGI_SMOOTH_MEDIAN, 5);
+ proc_save_mem(2);
+
+ /* hue */
+ proc_cmp(IMG_IFACE_CMP_HUE);
+ proc_save_mem(3);
+ proc_not();
+ proc_threshold(cv::THRESH_TOZERO_INV, 255 - 15);
+ proc_threshold(cv::THRESH_TOZERO, 255 - 23);
+ proc_threshold(cv::THRESH_BINARY_INV, 1);
+ proc_save_mem(4);
+
+ /* saturation */
+ proc_load_mem(2);
+ proc_cmp(IMG_IFACE_CMP_SATURATION);
+ proc_save_mem(5);
+ proc_threshold(cv::THRESH_TOZERO_INV, 163);
+ proc_threshold(cv::THRESH_TOZERO, 50);
+ proc_threshold(cv::THRESH_BINARY_INV, 1);
+ proc_save_mem(6);
+
+ /* value */
+ proc_load_mem(2);
+ proc_cmp(IMG_IFACE_CMP_VALUE);
+ proc_save_mem(7);
+ proc_threshold(cv::THRESH_TOZERO_INV, 240);
+ proc_threshold(cv::THRESH_TOZERO, 100);
+ proc_threshold(cv::THRESH_BINARY_INV, 1);
+ proc_save_mem(8);
+
+ /* Merge the components: H | S | V */
+ proc_save_ref();
+ proc_load_mem(6);
+ proc_or_2ref();
+ proc_save_ref();
+ proc_load_mem(4);
+ proc_or_2ref();
+ proc_dilate_erode(1);
+ proc_save_mem(9);
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time2: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+ }
+ /* Find bands: contours -> rectangles -> positions */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ /* Contours */
+ proc_contours(&contours, &hierarchy);
+
+ bool bands_nok;
+ bands_nok = contours.size() != 4;
+ if (bands_nok) {
+ status = RESISTOR_NOK_BANDS;
+ result_label(status);
+ return status;
+ }
+
+ for (i = 0; i < contours.size(); i++) {
+ blobs[i].found = true;
+ proc_min_area_rect(&(contours[i]), &rect, true);
+ blobs[i].x = rect.center.x;
+ blobs[i].y = rect.center.y;
+ }
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time3: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+
+ }
+ /* Read values of HSV on the center of each band */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ /* Sort blobs into bands (sort x) */
+ float tmp_x;
+ int tmp_i;
+ for (i = 0; i < contours.size(); i++) {
+ tmp_x = INFINITY;
+ for (j = 0; j < contours.size(); j++) {
+ if ((blobs[j].x < tmp_x) && (blobs[j].found)) {
+ tmp_x = blobs[j].x;
+ tmp_i = j;
+ }
+ }
+ blobs[tmp_i].found = false;
+ bands[i].blob = tmp_i;
+ bands[i].x = blobs[tmp_i].x;
+ bands[i].y = blobs[tmp_i].y;
+ }
+
+ /* Hue */
+ proc_load_mem(3);
+ for (i = 0; i < contours.size(); i++) {
+ proc_pixel_value(bands[i].x, bands[i].y, &(bands[i].h));
+ }
+ /* Saturation */
+ proc_load_mem(5);
+ for (i = 0; i < contours.size(); i++) {
+ proc_pixel_value(bands[i].x, bands[i].y, &(bands[i].s));
+ }
+ /* Value */
+ proc_load_mem(7);
+ for (i = 0; i < contours.size(); i++) {
+ proc_pixel_value(bands[i].x, bands[i].y, &(bands[i].v));
+ }
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time4: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+
+ }
+ /* Interpret colors */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ for (i = 0; (i < contours.size()) && (i < 5); i++) {
+ if (bands[i].s < 100) {
+ if (bands[i].v < 50) {
+ code[i] = '0';
+ } else if (bands[i].v > 200) {
+ code[i] = '9';
+ } else {
+ code[i] = '8';
+ }
+ } else {
+ if (bands[i].h < 20) {
+ if (bands[i].v > 220) {
+ code[i] = '3';
+ } else if (bands[i].v < 100) {
+ code[i] = '1';
+ } else {
+ code[i] = '2';
+ }
+ } else if (bands[i].h < 50) {
+ code[i] = '4';
+ } else if (bands[i].h < 93) {
+ code[i] = '5';
+ } else if (bands[i].h < 127) {
+ code[i] = '6';
+ } else if (bands[i].h < 161) {
+ code[i] = '7';
+ } else {
+ if (bands[i].v < 100) {
+ code[i] = '1';
+ } else {
+ code[i] = '2';
+ }
+ }
+ }
+ }
+
+#if 0
+ if (contours.size() == 3) {
+ /* Precission band not detected: gold */
+ code[3] = '4';
+ }
+#endif
+
+ /* Write bands' code into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Code: \"%s\"",
+ code);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time5: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+ }
+ /* Calculate resistor value */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ /* Base value */
+ int base;
+ base = 10 * (code[0] - '0') + (code[1] - '0');
+
+ /* Check that base value is a standard value */
+ int std_values [12] = {10,12,15,18,22,27,33,39,47,56,68,82};
+ bool std_value_nok;
+ std_value_nok = true;
+ for (i = 0; i < 12; i++) {
+ if (base == std_values[i]) {
+ std_value_nok = false;
+ }
+ }
+ if (std_value_nok) {
+ status = RESISTOR_NOK_STD_VALUE;
+ result_label(status);
+ return status;
+ }
+
+ /* Calculate resistance */
+ int power;
+ power = code[2] - '0';
+ resistance = base * pow(10, power);
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time6: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+ }
+ /* Calculate resistor tolerance */
+ {
+ /* Measure time */
+ time_0 = clock();
+
+ switch (code[3]) {
+ case '1':
+ tolerance = 1;
+ break;
+ case '2':
+ tolerance = 2;
+ break;
+ case '4':
+ tolerance = 5;
+ break;
+ case '8':
+ tolerance = 10;
+ break;
+ default:
+ status = RESISTOR_NOK_TOLERANCE;
+ result_label(status);
+ return status;
+ }
+
+ /* Write resistance value into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Resistance: %.2E ± %i% Ohm",
+ resistance, tolerance);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+
+ /* Measure time */
+ time_1 = clock();
+ times = ((double) time_1 - time_0) / CLOCKS_PER_SEC;
+ /* Write time into log */
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Time7: %.3lf", times);
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+ }
+#endif
+ status = COINS_OK;
+ result_coins(status);
+ return status;
+}
+
+static void result_coins (int status)
+{
+ /* Cleanup */
+
+ /* Write result into log */
+ char result [LOG_LINE_LEN];
+ switch (status) {
+ case COINS_OK:
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Label: OK");
+ break;
+ case COINS_NOK_COINS:
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Label: NOK_COINS");
+ break;
+ case COINS_NOK_OVERLAP:
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Label: NOK_OVERLAP");
+ break;
+ default:
+ snprintf(user_iface_log.line[user_iface_log.len], LOG_LINE_LEN,
+ "Label: NOK");
+ break;
+ }
+ user_iface_log.lvl[user_iface_log.len] = 0;
+ (user_iface_log.len)++;
+}
+
static int proc_resistor (void)
{
int status;
@@ -965,6 +1402,13 @@ static void proc_save_ref (void)
img_iface_act(IMG_IFACE_ACT_SAVE_REF, NULL);
}
+static void proc_local_max (void)
+{
+ img_iface_act(IMG_IFACE_ACT_LOCAL_MAX, NULL);
+
+ proc_show_img();
+}
+
static void proc_pixel_value (int x, int y, unsigned char *val)
{
struct Img_Iface_Data_Pixel_Value data;
@@ -1084,6 +1528,15 @@ static void proc_adaptive_threshold (int method, int type, int ksize)
proc_show_img();
}
+static void proc_cvt_color (int method)
+{
+ struct Img_Iface_Data_Cvt_Color data;
+ data.method = method;
+ img_iface_act(IMG_IFACE_ACT_CVT_COLOR, (void *)&data);
+
+ proc_show_img();
+}
+
static void proc_threshold (int type, int size)
{
struct Img_Iface_Data_Threshold data;
@@ -1094,11 +1547,9 @@ static void proc_threshold (int type, int size)
proc_show_img();
}
-static void proc_cvt_color (int method)
+static void proc_distance_transform (void)
{
- struct Img_Iface_Data_Cvt_Color data;
- data.method = method;
- img_iface_act(IMG_IFACE_ACT_CVT_COLOR, (void *)&data);
+ img_iface_act(IMG_IFACE_ACT_DISTANCE_TRANSFORM, NULL);
proc_show_img();
}
diff --git a/modules/user/inc/user_iface.h b/modules/user/inc/user_iface.h
index ea16f1a..18bd787 100644
--- a/modules/user/inc/user_iface.h
+++ b/modules/user/inc/user_iface.h
@@ -91,6 +91,7 @@
USER_IFACE_ACT_PROC = 0x4000,
USER_IFACE_ACT_PROC_LABEL,
+ USER_IFACE_ACT_PROC_COINS,
USER_IFACE_ACT_PROC_RESISTOR,
USER_IFACE_ACT_USRI = 0x8000,
diff --git a/modules/user/inc/user_iface.hpp b/modules/user/inc/user_iface.hpp
index 436782d..2b30ad3 100644
--- a/modules/user/inc/user_iface.hpp
+++ b/modules/user/inc/user_iface.hpp
@@ -91,6 +91,7 @@
USER_IFACE_ACT_PROC = 0x4000,
USER_IFACE_ACT_PROC_LABEL,
+ USER_IFACE_ACT_PROC_COINS,
USER_IFACE_ACT_PROC_RESISTOR,
USER_IFACE_ACT_USRI = 0x8000,
diff --git a/modules/user/src/user_clui.c b/modules/user/src/user_clui.c
index 8dbdda8..1c3eef3 100644
--- a/modules/user/src/user_clui.c
+++ b/modules/user/src/user_clui.c
@@ -149,6 +149,16 @@ static int usr_input (void)
case '3':
switch (ch[2]) {
case '0':
+ action = USER_IFACE_ACT_PROC_COINS;
+ break;
+ default:
+ action = USER_IFACE_ACT_FOO;
+ break;
+ }
+ break;
+ case '4':
+ switch (ch[2]) {
+ case '0':
action = USER_IFACE_ACT_PROC_RESISTOR;
break;
default:
@@ -495,7 +505,8 @@ static void show_help (void)
printf(" - Scan text (OCR): %s\n", "f40");
printf("Exercises:\n");
printf(" - Label: %s\n", "e10");
- printf(" - Resistor: %s\n", "e30");
+ printf(" - Coins: %s\n", "e30");
+ printf(" - Resistor: %s\n", "e40");
printf("Other:\n");
printf(" - Show OCR text: %s\n", "u1");
printf("Quit: %c\n", 'q');
diff --git a/modules/user/src/user_tui.c b/modules/user/src/user_tui.c
index fe28236..98b98a1 100644
--- a/modules/user/src/user_tui.c
+++ b/modules/user/src/user_tui.c
@@ -236,6 +236,19 @@ static int usr_input (void)
switch (ch) {
case '0':
+ action = USER_IFACE_ACT_PROC_COINS;
+ break;
+ default:
+ action = USER_IFACE_ACT_FOO;
+ break;
+ }
+ break;
+ case '4':
+ /* Resistor */
+ ch = wgetch(win_log);
+
+ switch (ch) {
+ case '0':
action = USER_IFACE_ACT_PROC_RESISTOR;
break;
default:
@@ -631,7 +644,8 @@ static void show_help (void)
mvwprintw(win_help, r++, c, " - Scan text (OCR): %s", "f40");
mvwprintw(win_help, r++, c, "Exercises:");
mvwprintw(win_help, r++, c, " - Label: %s", "e10");
- mvwprintw(win_help, r++, c, " - Resistor: %s", "e30");
+ mvwprintw(win_help, r++, c, " - Coins: %s", "e30");
+ mvwprintw(win_help, r++, c, " - Resistor: %s", "e40");
mvwprintw(win_help, r++, c, "Other:");
mvwprintw(win_help, r++, c, " - Show OCR: %s", "u1");
mvwprintw(win_help, r++, c, "Quit: %c", 'q');