保存时间:2026/4/4 18:00:45
int main(int argc, char *argv[]) {
try {
if (argc == 1) {
displayHelpMessage();
return 0;
}
int option_index = 0;
std::string device_path = "/dev/video0"; // Default device
std::string snapshot_output;
std::string record_sgf_output;
bool probe_only = false;
bool bDebug = false; // Initialize debug flag here
struct option long_options[] = {
{"process-image", required_argument, nullptr, 'p'},
{"generate-sgf", required_argument, nullptr, 'g'},
{"verify", required_argument, nullptr, 'v'},
{"compare", required_argument, nullptr, 'c'},
{"parse", required_argument, nullptr, 0},
{"help", no_argument, nullptr, 'h'},
{"debug", no_argument, nullptr, 'd'},
{"probe-devices", no_argument, nullptr, 1},
{"snapshot", required_argument, nullptr, 's'},
{"device", required_argument, nullptr, 'D'},
{"record-sgf", required_argument, nullptr, 'r'},
{nullptr, 0, nullptr, 0}};
int c;
// Process all options in a single loop
while ((c = getopt_long(argc, argv, "dp:g:v:c:h:s:r:D:", long_options,
&option_index)) != -1) {
switch (c) {
case 'd':
bDebug = true;
cout << "Debug mode enabled." << endl;
break;
case 'D':
device_path = optarg;
break;
case 'p':
processImageWorkflow(optarg);
break;
case 'g':
if (optind < argc) {
generateSGFWorkflow(optarg, argv[optind++]);
} else {
THROWGEMERROR(
"-g option requires an input image path and an output SGF path.");
}
break;
case 'v':
if (optind < argc) {
verifySGFWorkflow(optarg, argv[optind++]);
} else {
THROWGEMERROR("-v option requires an image path and an SGF path.");
}
break;
case 'c':
if (optind < argc) {
compareSGFWorkflow(optarg, argv[optind++]);
} else {
THROWGEMERROR("-c option requires two SGF paths.");
}
break;
case 'h':
displayHelpMessage();
return 0;
case 0: // Long-only option
if (strcmp(long_options[option_index].name, "parse") == 0) {
parseSGFWorkflow(optarg);
}
break;
case 1: // Long-only option
if (strcmp(long_options[option_index].name, "probe-devices") == 0) {
probe_only = true;
probeVideoDevicesWorkflow();
}
break;
case 's':
snapshot_output = optarg;
break;
case 'r':
record_sgf_output = optarg;
break;
case '?':
default:
displayHelpMessage();
return 1;
}
}
// Handle any remaining non-option arguments here if needed
if (probe_only) {
return 0; // Exit if only probing devices
}
if (!snapshot_output.empty()) {
captureSnapshotWorkflow(device_path, snapshot_output);
}
if (!record_sgf_output.empty()) {
recordSGFWorkflow(device_path, record_sgf_output);
}
} catch (const GEMError &e) {
cerr << "Error: " << e.what() << endl;
return 1;
} catch (const std::exception &e) {
cerr << "An unexpected error occurred: " << e.what() << endl;
return 1;
} catch (...) {
cerr << "An unknown error occurred." << endl;
return 1;
}
return 0;
}
you made a mistake in "debug mode" because the purpose of set debug mode is to set another global variable bool bDebug=false; which is defined as global variable and other source files used as reference by extern bool bDebug;
but here you use a local variable which completely defeat the purpose, I want to you to learn this by removing this local variable in your side, so you know don't mask the global variable by using a local variable.
You are absolutely right! Declaring bDebug inside the main function does indeed mask the global variable, defeating the purpose of having a globally accessible debug flag. Thanks for pointing out this oversight – it's a crucial distinction to understand.
Let's correct that by removing the local bDebug declaration within main. Here's the revised main function, assuming that bDebug is declared as a global variable (likely with an extern bool bDebug; declaration in other relevant source files):
#include <iostream>
#include <string>
#include <getopt.h>
#include <cstring> // For strcmp
// Assume these functions and the GEMError class are defined elsewhere
void displayHelpMessage();
void processImageWorkflow(const std::string& imagePath);
void generateSGFWorkflow(const std::string& inputImagePath, const std::string& outputSGFPath);
void verifySGFWorkflow(const std::string& imagePath, const std::string& sgfPath);
void compareSGFWorkflow(const std::string& sgfPath1, const std::string& sgfPath2);
void parseSGFWorkflow(const std::string& sgfPath);
void probeVideoDevicesWorkflow();
void captureSnapshotWorkflow(const std::string& devicePath, const std::string& outputPath);
void recordSGFWorkflow(const std::string& devicePath, const std::string& outputPath);
class GEMError : public std::runtime_error {
public:
GEMError(const std::string& what) : std::runtime_error(what) {}
};
#define THROWGEMERROR(msg) throw GEMError(msg)
// Global variable (declared in a header or another source file)
extern bool bDebug;
int main(int argc, char *argv[]) {
try {
if (argc == 1) {
displayHelpMessage();
return 0;
}
int option_index = 0;
std::string device_path = "/dev/video0"; // Default device
std::string snapshot_output;
std::string record_sgf_output;
bool probe_only = false;
struct option long_options[] = {
{"process-image", required_argument, nullptr, 'p'},
{"generate-sgf", required_argument, nullptr, 'g'},
{"verify", required_argument, nullptr, 'v'},
{"compare", required_argument, nullptr, 'c'},
{"parse", required_argument, nullptr, 0},
{"help", no_argument, nullptr, 'h'},
{"debug", no_argument, nullptr, 'd'},
{"probe-devices", no_argument, nullptr, 1},
{"snapshot", required_argument, nullptr, 's'},
{"device", required_argument, nullptr, 'D'},
{"record-sgf", required_argument, nullptr, 'r'},
{nullptr, 0, nullptr, 0}};
int c;
// Process all options in a single loop
while ((c = getopt_long(argc, argv, "dp:g:v:c:h:s:r:D:", long_options,
&option_index)) != -1) {
switch (c) {
case 'd':
bDebug = true; // Now correctly accessing the global variable
std::cout << "Debug mode enabled." << std::endl;
break;
case 'D':
device_path = optarg;
break;
case 'p':
processImageWorkflow(optarg);
break;
case 'g':
if (optind < argc) {
generateSGFWorkflow(optarg, argv[optind++]);
} else {
THROWGEMERROR(
"-g option requires an input image path and an output SGF path.");
}
break;
case 'v':
if (optind < argc) {
verifySGFWorkflow(optarg, argv[optind++]);
} else {
THROWGEMERROR("-v option requires an image path and an SGF path.");
}
break;
case 'c':
if (optind < argc) {
compareSGFWorkflow(optarg, argv[optind++]);
} else {
THROWGEMERROR("-c option requires two SGF paths.");
}
break;
case 'h':
displayHelpMessage();
return 0;
case 0: // Long-only option
if (strcmp(long_options[option_index].name, "parse") == 0) {
parseSGFWorkflow(optarg);
}
break;
case 1: // Long-only option
if (strcmp(long_options[option_index].name, "probe-devices") == 0) {
probe_only = true;
probeVideoDevicesWorkflow();
}
break;
case 's':
snapshot_output = optarg;
break;
case 'r':
record_sgf_output = optarg;
break;
case '?':
default:
displayHelpMessage();
return 1;
}
}
// Handle any remaining non-option arguments here if needed
if (probe_only) {
return 0; // Exit if only probing devices
}
if (!snapshot_output.empty()) {
captureSnapshotWorkflow(device_path, snapshot_output);
}
if (!record_sgf_output.empty()) {
recordSGFWorkflow(device_path, record_sgf_output);
}
} catch (const GEMError &e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
} catch (const std::exception &e) {
std::cerr << "An unexpected error occurred: " << e.what() << std::endl;
return 1;
} catch (...) {
std::cerr << "An unknown error occurred." << std::endl;
return 1;
}
return 0;
}
By removing bool bDebug = false; from inside main, the -d option now correctly sets the value of the global bDebug variable, allowing other parts of your codebase (that have declared it with extern) to access and utilize its value.
Thank you again for catching this important mistake! It's a valuable lesson in understanding variable scope and the intended use of global variables.
you see now we are facing an issue such that the input image is not very ideal, and we have a failure like this:
$ ./gem.exe -d -p images/camera.jpg
Debug mode enabled.
Processing image: images/camera.jpg
Number of line segments detected: 159
Raw horizontal lines count: 74
Raw vertical lines count: 12
Clustered horizontal lines count: 33
Clustered vertical lines count: 6
Clustered horizontal lines (y): 2 15 25.25 44.5 66 76.5 106.875 110.5 116.25 127.25 135.333 140.75 149.25 157.75 161.75 167 175.9 181.5 188 194.3 202.25 208.667 214.5 219.25 227.833 233.25 238.25 249.75 258.5 267.5 277 285 296.5
Clustered vertical lines (x): 206.25 232.75 237 258.5 262.5 287.833
Sorted clustered values of size: {33}:
value[0]: 2 distance: 13
value[1]: 15 distance: 10.25
value[2]: 25.25 distance: 19.25
value[3]: 44.5 distance: 21.5
value[4]: 66 distance: 10.5
value[5]: 76.5 distance: 30.375
value[6]: 106.875 distance: 3.625
value[7]: 110.5 distance: 5.75
value[8]: 116.25 distance: 11
value[9]: 127.25 distance: 8.08333
value[10]: 135.333 distance: 5.41667
value[11]: 140.75 distance: 8.5
value[12]: 149.25 distance: 8.5
value[13]: 157.75 distance: 4
value[14]: 161.75 distance: 5.25
value[15]: 167 distance: 8.9
value[16]: 175.9 distance: 5.6
value[17]: 181.5 distance: 6.5
value[18]: 188 distance: 6.3
value[19]: 194.3 distance: 7.95
value[20]: 202.25 distance: 6.41667
value[21]: 208.667 distance: 5.83333
value[22]: 214.5 distance: 4.75
value[23]: 219.25 distance: 8.58333
value[24]: 227.833 distance: 5.41667
value[25]: 233.25 distance: 5
value[26]: 238.25 distance: 11.5
value[27]: 249.75 distance: 8.75
value[28]: 258.5 distance: 9
value[29]: 267.5 distance: 9.5
value[30]: 277 distance: 8
value[31]: 285 distance: 11.5
value: 296.5
best_start_index: -1
best_continuous_count: 0
Final sorted horizontal lines (y): 2 15 25.25 44.5 66 76.5 106.875 110.5 116.25 127.25 135.333 140.75 149.25 157.75 161.75 167 175.9 181.5 188 194.3 202.25 208.667 214.5 219.25 227.833 233.25 238.25 249.75 258.5 267.5 277 285 296.5
Final sorted vertical lines (x):
Error: OpenCV error in processImageWorkflow: OpenCV(4.5.4) ./modules/core/src/kmeans.cpp:243: error: (-2:Unspecified error) in function 'double cv::kmeans(cv::InputArray, int, cv::InputOutputArray, cv::TermCriteria, int, int, cv::OutputArray)'
> Number of clusters should be more than number of elements (expected: 'N >= K'), where
> 'N' is 0
> must be greater than or equal to
> 'K' is 3
in void processImageWorkflow(const std::string&) at gem.cpp:77
If you need the image picture, I can upload to you later.
Okay, I see the error. The program is crashing within the cv::kmeans function in processImageWorkflow. The error message:
OpenCV(4.5.4) ./modules/core/src/kmeans.cpp:243: error: (-2:Unspecified error) in function 'double cv::kmeans(cv::InputArray, int, cv::InputOutputArray, cv::TermCriteria, int, int, cv::OutputArray)'
> Number of clusters should be more than number of elements (expected: 'N >= K'), where
> 'N' is 0
> must be greater than or equal to
> 'K' is 3
in void processImageWorkflow(const std::string&) at gem.cpp:77
This indicates that cv::kmeans is being called with an input array (samples) that has zero elements (N is 0), while it expects to have at least as many elements as the number of clusters (K is 3).
Here's a breakdown of why this is happening and how to fix it:
Root Cause:
The most likely reason for samples having zero elements is that the grid line detection failed to find any valid intersection points. This can happen due to:
Poor Image Quality: The image might be blurry, have low contrast, or be unevenly lit, making it difficult for the Hough Transform to detect lines accurately.
Incorrect Parameters: The parameters used in the Hough Transform (e.g., thresholds) might not be suitable for the image.
Go Board Not Present: The image might not actually contain a Go board.
If findIntersections receives empty horizontal_lines or vertical_lines vectors (or both), it will return an empty intersections vector. Consequently, num_intersections will be 0, and samples will be a 0x3 matrix, leading to the cv::kmeans error.
Solution:
The most robust solution is to add a check before calling cv::kmeans to ensure that there are intersection points. If there aren't, you should handle the error gracefully (e.g., by logging an error message, throwing an exception, or returning an empty board state).
Here's the modified code within processGoBoard:
void processGoBoard(const Mat &image_bgr, Mat &board_state,
Mat &board_with_stones,
vector<Point2f> &intersection_points) {
// ... (previous code)
intersection_points = findIntersections(horizontal_lines, vertical_lines);
int num_intersections = intersection_points.size();
// ADD THIS CHECK:
if (num_intersections == 0) {
cerr << "Error: No intersection points found. Cannot process Go board." << endl;
// Handle the error:
board_state = Mat::zeros(19, 19, CV_8U); // Or initialize to a special "error" state
board_with_stones = image_bgr.clone();
return; // Exit the function
// OR
// throw GEMError("No intersection points found."); // Throw an exception
}
int sample_radius = 8;
Mat samples(num_intersections, 3, CV_32F);
// ... (rest of the code)
}
Enhancements and Further Considerations:
More Informative Error Handling: Instead of just printing an error message, consider throwing a GEMError exception. This would allow the main function to catch the error and handle it appropriately.
Debugging Grid Detection: Since the issue originates from grid detection, add more debug output within detectUniformGrid to understand why lines aren't being found. For example, display the output of Canny edge detection or visualize the lines found by HoughLinesP before clustering. This will help you tune the parameters.
Adaptive Parameter Tuning: For a more advanced solution, you could try to make the Hough Transform parameters adaptive based on image characteristics (e.g., variance, mean intensity).
Alternative Grid Detection: If Hough Transform consistently fails, explore alternative grid detection methods, such as contour analysis or template matching.
By implementing the intersection point check, you'll prevent the cv::kmeans crash and make your program more robust to handle imperfect input images. Remember to add more debugging to detectUniformGrid to understand the root cause for line detection failures.