
Coming in 2027: SCX AI
SCX AI is a free software update for all GSA Golf launch monitor models

For some time now (over a year) I've been experimenting and testing AI methods and AI generated code of camera image ball and club tracking.
I'm at a point now where I can start integrating these methods and code into the current CP software.
Once completed, this will vastly improve the accuracy of the SCX tracking - in particular - ball back and side spin detection
and make the SCX equally as good as any other launch monitor on the market today that is in the $10,000 plus range
while still keeping the SCX super low starting price of just $1,699.
As most probably already know, AI is making leaps and bounds in development with Artificial General Intelligence (AGI) only a few months away
and Superintelligence (ASI) only a couple of years away.
Even in its current state of development, AI can solve most problems it is presented with,
while AGI matches or surpasses human cognitive capabilities across virtually all domains.
And ASI is estimated to be more than a thousand times more intelligent than the combined intelligence of all human intelligence that ever existed.
It has been stated by most AI companies that ASI will be able to solve any problem presented to it.
Including: Curing all known diseases including Cancer and stopping the aging process so that we live forever.
Of course, when in the wrong hands, rogue AI can equally totally disrupt and destroy our world as we know it.
e.g. totally disrupting the internet, taking over the SWIFT banking system so that bank accounts are depleted,
shutting down major infrastructures causing major power blackouts, developing thousands of Viruses far more dangerous than Covid
and unleashing them on human society etc,etc...
In contrast to HAL ( the rogue super computer in the ScFi film 2001: A Space Odyssey) where the crew could simply pull the plug on its main brain functions,
rogue AI can self replicate itself thousands of times on thoudsands of servers around the world. i.e. you can't stop it.
One can only hope this doesn't happen in our lives but I fear it will sooner or later.
Anyway, bearing all this super intelligence in mind, I have no doubt that when presented with a super simple task such as to precisely measure
a golf ball's spin from a camera is absolutely peanuts.
![]()
Main Objectives
1. Increase acurracy of ball back and side spin
2. Detect club face angle, path and speed without the requirement to use markings on the club
3. Increase ball speed, VLA and HLA accuracy
![]()
In Detail
![]()
Image matching
1. Increase acurracy of ball back and side spin
2. Detect club face angle, path and speed without the requirement to use markings on the club
Both these tasks require image matching
The current "Template" matching method will be replaced by the new "Feature" matching method
AI generated code example for "Feature" matching :
AI says:
"If the sub-image has been distorted, tilted, or scaled down, template matching will fail.
Instead, you can use SIFT (Scale-Invariant Feature Transform) to find unique mathematical anchor points in both images and pair them up."
if (img_template.empty() || img_main.empty()) {
std::cout << "Error loading images." << std::endl;
return -1;
}
// 2. Detect keypoints and calculate descriptors using SIFT
cv::Ptr<cv::SIFT> detector = cv::SIFT::create();
std::vector<cv::KeyPoint> keypoints_template, keypoints_main;
cv::Mat descriptors_template, descriptors_main;
detector->detectAndCompute(img_template, cv::noArray(), keypoints_template, descriptors_template);
detector->detectAndCompute(img_main, cv::noArray(), keypoints_main, descriptors_main);
// 3. Match descriptor vectors using a FLANN-based matcher
cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create(cv::DescriptorMatcher::FLANNBASED);
std::vector<std::vector<cv::DMatch>> knn_matches;
matcher->knnMatch(descriptors_template, descriptors_main, knn_matches, 2);
// 4. Filter matches using Lowe's ratio test
std::vector<cv::DMatch> good_matches;
for (size_t i = 0; i < knn_matches.size(); i++) {
if (knn_matches[i][0].distance < 0.7 * knn_matches[i][1].distance) {
good_matches.append(knn_matches[i][0]);
}
}
// 5. Draw and save the matching lines
cv::Mat img_matches;
cv::drawMatches(img_template, keypoints_template, img_main, keypoints_main,
good_matches, img_matches, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("feature_matches_cpp.jpg", img_matches);
std::cout << "Found " << good_matches.size() << " good feature matches." << std::endl;
return 0;
}
![]()
Mapping a dot on a 2D image of a ball to its true 3D position on the surface of the ball
Note: True dot mapping is required to determin the amount of both back and side spin rotation
AI says:
"To map a dot on a 2D image of a spherical ball to its true 3D position on the surface of that ball, you must correct for perspective distortion.
Because a ball is a 3D sphere, a dot near the edge of the image appears squished and closer to the center than it actually is.
To solve this, you need to calculate the dot's true 3D spherical coordinates (latitude and longitude) relative to the center of the ball."
Here is how to implement this calculation using OpenCV in C++.
This code takes the detected center of the ball, its radius, and the pixel coordinates of the dot, then outputs its true 3D surface vector.
AI generated code example for "Dot" mapping :
// Function to calculate true 3D surface position from 2D pixel coordinates
Point3D getTrue3DPosition(cv::Point2f ballCenter, double ballRadius, cv::Point2f dotPixel) {
// 1. Shift origin to the center of the ball
double x_rel = dotPixel.x - ballCenter.x;
double y_rel = ballCenter.y - dotPixel.y; // Invert Y because image pixels go down, Cartesian goes up
// 2. Calculate the 2D pixel distance from the center
double r = std::sqrt(x_rel * x_rel + y_rel * y_rel);
// Handle edge case: if the dot is mathematically outside the radius due to pixel blur
if (r > ballRadius) {
r = ballRadius;
}
// 3. Calculate the true Z depth (facing the camera)
double z_rel = std::sqrt((ballRadius * ballRadius) - (r * r));
// 4. Normalize the vector to get unit coordinates on the sphere's surface (-1.0 to 1.0)
Point3D truePos;
truePos.x = x_rel / ballRadius;
truePos.y = y_rel / ballRadius;
truePos.z = z_rel / ballRadius;
return truePos;
}
int main() {
// Example: A ball centered at (300, 300) with a 100-pixel radius
cv::Point2f center(300.0f, 300.0f);
double radius = 100.0;
// A dot detected at pixel (350, 250)
cv::Point2f dot(350.0f, 250.0f);
Point3D surfacePos = getTrue3DPosition(center, radius, dot);
std::cout << "True 3D Vector on Sphere Surface:\n";
std::cout << "X: " << surfacePos.x << " (Right)\n";
std::cout << "Y: " << surfacePos.y << " (Up)\n";
std::cout << "Z: " << surfacePos.z << " (Depth toward camera)\n";
// Convert to Latitude / Longitude angles if needed
double longitude = std::atan2(surfacePos.x, surfacePos.z) * 180.0 / CV_PI;
double latitude = std::asin(surfacePos.y) * 180.0 / CV_PI;
std::cout << "\nSpherical Coordinates:\nLon: " << longitude << "°, Lat: " << latitude << "°\n";
return 0;
}
![]()
How to determin the amount of back and side spin rotation of a ball given 2 dots on 2 2D images of the ball
AI says:
If you track two distinct dots (A and B) across both images, you can build a unique 3D coordinate system (a "triad") for each image.
Multiplying these coordinate systems gives you the exact 3x3 rotation matrix, including any spinning.
AI generated code example for 3D rotation
// Expects normalized 3D positions for two dots (A and B) in both image states
cv::Mat getFull3DRotation(cv::Point3f A1, cv::Point3f B1, cv::Point3f A2, cv::Point3f B2) {
// Helper lambda to construct an orthogonal 3D coordinate frame matrix [X | Y | Z]
auto buildFrame = [](cv::Point3f A, cv::Point3f B) {
cv::Point3f x_axis = A; // Use dot A as the primary anchor
cv::Point3f z_axis = A.cross(B); // Perpendicular to both dots
z_axis = z_axis * (1.0 / cv::norm(z_axis));
cv::Point3f y_axis = z_axis.cross(x_axis); // Completes the right-handed frame
cv::Mat M = (cv::Mat_<double>(3, 3) <<
x_axis.x, y_axis.x, z_axis.x,
x_axis.y, y_axis.y, z_axis.y,
x_axis.z, y_axis.z, z_axis.z);
return M;
};
// 1. Build the 3D frame for Image 1 and Image 2
cv::Mat M1 = buildFrame(A1, B1);
cv::Mat M2 = buildFrame(A2, B2);
// 2. Compute the final rotation matrix: R = M2 * Matrix_Transpose(M1)
cv::Mat R = M2 * M1.t();
return R;
}
How to read the resulting Matrix R
Once you have the 3x3 rotation matrix R, you can extract the exact human-readable breakdown of the movement:
Total Angular Travel:\(\text{Total\ Angle}=\arccos \left(\frac{\text{Trace}(R)-1}{2}\right)\)(In OpenCV: double angle = acos((cv::trace(R)[0] - 1.0) / 2.0);)
Euler Angles (Pitch, Roll, Yaw): You can convert R directly into degrees of rotation relative to your camera's viewport axes.
To help write the code that extracts the specific angles you need, tell me:Are you looking for a single total angle of movement or the specific pitch/yaw/roll values?
If you have more than 2 dots (a cloud of dots), would you like to see how to use Singular Value Decomposition (SVD) to filter out measurement noise?
![]()
Tel: 239 529 3060
![]()