Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for Issue #35 - Improved Golf Sim Arming Processing #42

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
24 changes: 12 additions & 12 deletions Software/LMSourceCode/ImageProcessing/ball_image_proc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2468,8 +2468,8 @@ namespace golf_sim {

// Re-center the ball's x and y position in the new, smaller picture
// This will change the ball that was sent in
ball.set_x( (int)std::round(rInc + ball.measured_radius_pixels_));
ball.set_y( (int)std::round(rInc + ball.measured_radius_pixels_));
ball.set_x( (float)std::round(rInc + ball.measured_radius_pixels_));
ball.set_y( (float)std::round(rInc + ball.measured_radius_pixels_));

cv::Point offset_sub_to_full;
cv::Point offset_full_to_sub;
Expand Down Expand Up @@ -2581,12 +2581,12 @@ namespace golf_sim {
// Adjust relevant ball radius information accordingly
local_ball1.measured_radius_pixels_ = local_ball1.measured_radius_pixels_ * ball1RadiusMultiplier;
local_ball1.ball_circle_[2] = local_ball1.ball_circle_[2] * (float)ball1RadiusMultiplier;
local_ball1.set_x( (int)((double)local_ball1.x() * ball1RadiusMultiplier));
local_ball1.set_y( (int)((double)local_ball1.y() * ball1RadiusMultiplier));
local_ball1.set_x( (float)((double)local_ball1.x() * ball1RadiusMultiplier));
local_ball1.set_y( (float)((double)local_ball1.y() * ball1RadiusMultiplier));
local_ball2.measured_radius_pixels_ = local_ball2.measured_radius_pixels_ * ball2RadiusMultiplier;
local_ball2.ball_circle_[2] = local_ball2.ball_circle_[2] * (float)ball2RadiusMultiplier;
local_ball2.set_x( (int)((double)local_ball2.x() * ball2RadiusMultiplier));
local_ball2.set_y( (int)((double)local_ball2.y() * ball2RadiusMultiplier));
local_ball2.set_x( (float)((double)local_ball2.x() * ball2RadiusMultiplier));
local_ball2.set_y( (float)((double)local_ball2.y() * ball2RadiusMultiplier));


std::vector < cv::Point > center1 = { cv::Point{(int)local_ball1.x(), (int)local_ball1.y()} };
Expand All @@ -2596,7 +2596,7 @@ namespace golf_sim {
LoggingTools::DebugShowImage("Ball2 Image", ball_image2, center2);
GS_LOG_TRACE_MSG(trace, "Updated (local) ball2 data: " + local_ball2.Format());

int calibrated_binary_threshold = 0;
float calibrated_binary_threshold = 0;
cv::Mat ball_image1DimpleEdges = ApplyGaborFilterToBall(ball_image1, local_ball1, calibrated_binary_threshold);
// Suggest the same binary threshold between the images as a starting point for the second ball - they are probably similar
cv::Mat ball_image2DimpleEdges = ApplyGaborFilterToBall(ball_image2, local_ball2, calibrated_binary_threshold, calibrated_binary_threshold);
Expand Down Expand Up @@ -3112,7 +3112,7 @@ namespace golf_sim {
return kernel;
}

cv::Mat BallImageProc::ApplyGaborFilterToBall(const cv::Mat& image_gray, const GolfBall& ball, int & calibrated_binary_threshold, int prior_binary_threshold) {
cv::Mat BallImageProc::ApplyGaborFilterToBall(const cv::Mat& image_gray, const GolfBall& ball, float & calibrated_binary_threshold, float prior_binary_threshold) {
// TBD - Not sure we will ever need the ball information?
CV_Assert( (image_gray.type() == CV_8UC1) );

Expand Down Expand Up @@ -3177,19 +3177,19 @@ namespace golf_sim {
if (ratheting_threshold_down)
{
if (kGaborMinWhitePercent - white_percent > 5) {
binary_threshold = (float)binary_threshold - 1.0;
binary_threshold = binary_threshold - 1.0F;
}
else {
binary_threshold = (float)binary_threshold - 0.5;
binary_threshold = binary_threshold - 0.5F;
}
GS_LOG_TRACE_MSG(trace, "Trying lower gabor binary_threshold setting of " + std::to_string(binary_threshold) + " for better balance.");
}
else {
if (white_percent - kGaborMaxWhitePercent > 5) {
binary_threshold = (float)binary_threshold + 1.0;
binary_threshold = binary_threshold + 1.0F;
}
else {
binary_threshold = (float)binary_threshold + 0.5;
binary_threshold = binary_threshold + 0.5F;
}
GS_LOG_TRACE_MSG(trace, "Trying higher gabor binary_threshold setting of " + std::to_string(binary_threshold) + " for better balance.");
}
Expand Down
2 changes: 1 addition & 1 deletion Software/LMSourceCode/ImageProcessing/ball_image_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ class BallImageProc

// If prior_binary_threshold < 0, then there is no prior threshold and a new one will be determined and returns
// in the calibrated_binary_threshold variable.
static cv::Mat ApplyGaborFilterToBall(const cv::Mat& img, const GolfBall& ball, int& calibrated_binary_threshold, int prior_binary_threshold = -1);
static cv::Mat ApplyGaborFilterToBall(const cv::Mat& img, const GolfBall& ball, float& calibrated_binary_threshold, float prior_binary_threshold = -1);

// Applies the gabor filter with the specified parameters and returns the final image and white percentage
static cv::Mat ApplyTestGaborFilter(const cv::Mat& img_f32,
Expand Down
8 changes: 4 additions & 4 deletions Software/LMSourceCode/ImageProcessing/golf_ball.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ void GolfBall::PrintBallFlightResults() const {
void GolfBall::AverageBalls(const std::vector<GolfBall>& ball_vector, GolfBall& averaged_ball) {
double number_balls = (double)ball_vector.size();

averaged_ball.set_x(0);
averaged_ball.set_y(0);
averaged_ball.set_x(0.0F);
averaged_ball.set_y(0.0F);
averaged_ball.velocity_ = 0;
averaged_ball.position_deltas_ball_perspective_ = { 0, 0, 0 };
averaged_ball.angles_ball_perspective_ = { 0,0 };
Expand All @@ -237,11 +237,11 @@ void GolfBall::AverageBalls(const std::vector<GolfBall>& ball_vector, GolfBall&
averaged_ball.velocity_ += b.velocity_ / number_balls;

long x = averaged_ball.x();
x += b.x() / number_balls;
x += (long)(b.x() / number_balls);
averaged_ball.set_x(x);

long y = averaged_ball.y();
y += b.y() / number_balls;
y += (long)(b.y() / number_balls);
averaged_ball.set_y(y);

// NOTE - Not clear how often the position deltas should be averaged?
Expand Down
6 changes: 5 additions & 1 deletion Software/LMSourceCode/ImageProcessing/golf_ball.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ class GolfBall {
long y() const { return y_; }; // In pixels in openCV coordinate system

void set_x(long x) { x_ = x; ball_circle_[0] = (float)x; };
void set_y(long y) { y_ = y; ball_circle_[1] = (float)y;};
void set_y(long y) { y_ = y; ball_circle_[1] = (float)y; };

// Allows type-safe calls that use ball data to set ball data
void set_x(float x) { x_ = (long) x; ball_circle_[0] = x; };
void set_y(float y) { y_ = (long) y; ball_circle_[1] = y; };

// The circle where the ball exists on the relevant image. Could (in theory?) be different from the
// more definitive x and y and radius_at_calibration_pixels_!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@
@WebServlet("/monitor")
public class MonitorServlet extends HttpServlet {


// NOTE - these should reflect types in gs_ipc_result
public enum GsIPCResultType {
kUnknown,
kInitializing,
kWaitingForBallToAppear,
kWaitingForSimulatorArmed,
kPausingForBallStabilization,
kMultipleBallsPresent,
kBallPlacedAndReadyForHit,
Expand Down Expand Up @@ -363,6 +364,7 @@ public String Format(HttpServletRequest request) {
s += " Log Messages:\n" + log_messages_console_str;
}

// Set the GUI top color to be white as a default. Special cases follow
request.setAttribute("ball_ready_color", "\"item1 grid-item-text-center-white\"");

// System.out.println("Checking for Ball placed");
Expand All @@ -373,6 +375,12 @@ public String Format(HttpServletRequest request) {
request.setAttribute("ball_ready_color", "\"item1 grid-item-text-center-green\"");
}

if (result_type_ == GsIPCResultType.kWaitingForSimulatorArmed)
{
System.out.println("kWaitingForSimulatorArmed - setting ball_ready_color to 'item1 grid-item-text-center-yellow'");
request.setAttribute("ball_ready_color", "\"item1 grid-item-text-center-yellow\"");
}

// If the result is a hit, then include IMG images
// Even if it's an error, we might still find the images (if they
// exist) to be useful
Expand All @@ -397,6 +405,7 @@ else if (result_type_ == GsIPCResultType.kError )
}
else if (result_type_ == GsIPCResultType.kWaitingForBallToAppear )
{
// Make sure the user can see what the monitor is seeing, especially if the user may have placed the ball outside the ball search area
request.setAttribute("images", "<img src=\"" + kWebServerTomcatShareDirectory + "/" + kWebServerBallSearchAreaImage + "\" alt=\"Ball Search Area\" width = \"360\" heigth=\"272\" />");
}

Expand Down Expand Up @@ -441,6 +450,11 @@ public String FormatResultType(GsIPCResultType t) {
break;
}

case kWaitingForSimulatorArmed: {
s = "Waiting for the simulator to be armed";
break;
}

case kInitializing: {
s = "Initializing launch monitor.";
break;
Expand Down Expand Up @@ -905,6 +919,7 @@ else if (message instanceof BytesMessage) {
if (new_result.speed_mpers_ <= 0 &&
(new_result.result_type_ != GsIPCResultType.kBallPlacedAndReadyForHit &&
new_result.result_type_ != GsIPCResultType.kInitializing &&
new_result.result_type_ != GsIPCResultType.kWaitingForSimulatorArmed &&
new_result.result_type_ != GsIPCResultType.kError
)) {
// Don't replace the current result, as the user may still be looking at it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@
border-radius: 10px;
}

.grid-item-text-center-yellow {
background-color: #FFFF00;
border: 2px solid rgb(0, 0, 0);
border-width: medium;
font-size: 30px;
text-align: center;
padding: 10px;
padding-left: 20px;
font-size: 40px;
border-radius: 10px;
}

.grid-item-empty-row {
background-color: rgba(255, 255, 255, 0.8);
text-align: center;
Expand Down
10 changes: 9 additions & 1 deletion Software/LMSourceCode/ImageProcessing/gs_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ namespace golf_sim {
PossibleEvent possible_event;

GolfSimEvent::BeginWaitingForBallPlaced* beginWaitingForBallPlaced = nullptr;
GolfSimEvent::SimulatorIsArmed* simulatorIsArmed = nullptr;
GolfSimEvent::BeginWaitingForSimulatorArmed* beginWaitingForSimulatorArmed = nullptr;
GolfSimEvent::EventLoopTick* eventLoopTick = nullptr;
GolfSimEvent::BallStabilized* ballStabilized = nullptr;
GolfSimEvent::BallHit* ballHit = nullptr;
Expand All @@ -66,7 +68,13 @@ namespace golf_sim {
GolfSimEvent::ArmCamera2MessageReceived* armCamera2MessageReceived = nullptr;


if ((beginWaitingForBallPlaced = dynamic_cast<GolfSimEvent::BeginWaitingForBallPlaced*>(event))) {
if ((beginWaitingForSimulatorArmed = dynamic_cast<GolfSimEvent::BeginWaitingForSimulatorArmed*>(event))) {
possible_event = *beginWaitingForSimulatorArmed;
}
else if ((simulatorIsArmed = dynamic_cast<GolfSimEvent::SimulatorIsArmed*>(event))) {
possible_event = *simulatorIsArmed;
}
else if ((beginWaitingForBallPlaced = dynamic_cast<GolfSimEvent::BeginWaitingForBallPlaced*>(event))) {
possible_event = *beginWaitingForBallPlaced;
}
else if ((eventLoopTick = dynamic_cast<GolfSimEvent::EventLoopTick*>(event))) {
Expand Down
31 changes: 26 additions & 5 deletions Software/LMSourceCode/ImageProcessing/gs_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
*/

// This module defines the events, event queue, and associated processing for the various
// types of events that occur in the launch monitor system.
// types of events that occur within the launch monitor system.
// These events largely drive the transitions in the system's finite state machine
// These events are separate from IPC events that deal with external messaging.

#pragma once

Expand Down Expand Up @@ -44,6 +45,15 @@ namespace golf_sim {
virtual std::string Format() override { return "EventLoopTick"; };
};

class BeginWatchingForBallHit : public GolfSimEventBase
{
public:
BeginWatchingForBallHit() {};
~BeginWatchingForBallHit() {};

virtual std::string Format() override { return "BeginWatchingForBallHit"; };
};

class BeginWaitingForBallPlaced : public GolfSimEventBase
{
public:
Expand Down Expand Up @@ -96,14 +106,22 @@ namespace golf_sim {
GsIPCControlMsgType message_type_;
};

class BeginWaitingForSimulatorArmed : public GolfSimEventBase
{
public:
BeginWaitingForSimulatorArmed() {};
~BeginWaitingForSimulatorArmed() {};

class BeginWatchingForBallHit : public GolfSimEventBase
virtual std::string Format() override { return "BeginWaitingForSimulatorArmed"; };
};

class SimulatorIsArmed : public GolfSimEventBase
{
public:
BeginWatchingForBallHit() {};
~BeginWatchingForBallHit() {};
SimulatorIsArmed() {};
~SimulatorIsArmed() {};

virtual std::string Format() override { return "BeginWatchingForBallHit"; };
virtual std::string Format() override { return "SimulatorIsArmed"; };
};

class CheckForCam2ImageReceived : public GolfSimEventBase
Expand All @@ -115,6 +133,7 @@ namespace golf_sim {
virtual std::string Format() override { return "CheckForCam2ImageReceived"; };
};

// TBD - this error event isn't really handled properly yet
class FoundMultipleBalls : public GolfSimEventBase
{
public:
Expand Down Expand Up @@ -207,6 +226,8 @@ namespace golf_sim {
}

using PossibleEvent = std::variant< GolfSimEvent::EventLoopTick,
GolfSimEvent::BeginWaitingForSimulatorArmed,
GolfSimEvent::SimulatorIsArmed,
GolfSimEvent::BeginWaitingForBallPlaced,
GolfSimEvent::CheckForBallStable,
GolfSimEvent::BallStabilized,
Expand Down
67 changes: 62 additions & 5 deletions Software/LMSourceCode/ImageProcessing/gs_fsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,24 @@ namespace golf_sim {

GolfSimState onEvent(const state::InitializingCamera1System& initializing,
const GolfSimEvent::Restart& restart) {
GS_LOG_MSG(debug, "GolfSim state transition: Initializing - Received Restart - Next state WaitingForBall. ");

GolfSimEventElement beginWaitingForBallPlacedEvent{ new GolfSimEvent::BeginWaitingForBallPlaced{ } };
GolfSimEventQueue::QueueEvent(beginWaitingForBallPlacedEvent);
GS_LOG_MSG(debug, "GolfSim state transition: Initializing - Received Restart - Next state WaitingForSimArmed or WaitingForBall. ");

// Let the monitor interface know what's happening
GsUISystem::SendIPCStatusMessage(GsIPCResultType::kInitializing);

return state::WaitingForBall{ std::chrono::steady_clock::now() };
// If we're already armed, just start waiting for a ball to appear.
if (GsSimInterface::GetAllSystemsArmed()) {
GolfSimEventElement beginWaitingForBallPlacedEvent{ new GolfSimEvent::BeginWaitingForBallPlaced{ } };
GolfSimEventQueue::QueueEvent(beginWaitingForBallPlacedEvent);

return state::WaitingForBall{ std::chrono::steady_clock::now() };
}

GolfSimEventElement beginWaitingForSimulatorArmedEvent{ new GolfSimEvent::BeginWaitingForSimulatorArmed{ } };
GolfSimEventQueue::QueueEvent(beginWaitingForSimulatorArmedEvent);

return state::WaitingForSimulatorArmed{ std::chrono::steady_clock::now() };

}


Expand Down Expand Up @@ -301,6 +310,7 @@ namespace golf_sim {
// to figure out what it needs to display
GsUISystem::ClearWebserverImages();

// TBD - Probably remove. Pre-image subtraction was an idea that never panned out as well as we'd hoped.
if (GolfSimCamera::kUsePreImageSubtraction) {
return state::WaitingForCamera2PreImage{ std::chrono::steady_clock::now(), ball, img };
}
Expand Down Expand Up @@ -334,6 +344,50 @@ namespace golf_sim {
camera2PreImageReceived.GetBallFlightPreImage() };
}



/*********** WaitingForSimulatorArmed ************/

GolfSimState onEvent(const state::WaitingForSimulatorArmed& waitingForSimulatorArmed,
const GolfSimEvent::BeginWaitingForSimulatorArmed& beginWaitingForSimulatorArmed) {
GS_LOG_MSG(debug, "GolfSim state transition: WaitingForSimulatorArmed - Received SimulatorArmed.");

// Let the monitor interface know what's happening so it can alert the user
GsUISystem::SendIPCStatusMessage(GsIPCResultType::kWaitingForSimulatorArmed);

// Wait a moment so that we're not spinning too much
sleep(1);

if (GsSimInterface::GetAllSystemsArmed()) {
GolfSimEventElement beginWaitingForBallPlacedEvent{ new GolfSimEvent::BeginWaitingForBallPlaced{ } };
GolfSimEventQueue::QueueEvent(beginWaitingForBallPlacedEvent);

return state::WaitingForBall{ std::chrono::steady_clock::now() };
}

// Otherwise, keep in waiting state
GolfSimEventElement nextBeginWaitingForSimulatorArmed{ new GolfSimEvent::BeginWaitingForSimulatorArmed{ } };
GolfSimEventQueue::QueueEvent(nextBeginWaitingForSimulatorArmed);

return state::WaitingForSimulatorArmed{ std::chrono::steady_clock::now() };
}

// TBD - Not certain we are going to use this state
GolfSimState onEvent(const state::WaitingForSimulatorArmed& waitingForSimulatorArmed,
const GolfSimEvent::SimulatorIsArmed& simulatorArmed) {
GS_LOG_MSG(debug, "GolfSim state transition: WaitingForSimulatorArmed - Received SimulatorArmed.");

// Let the monitor interface know what's happening so it can alert the user
GsUISystem::SendIPCStatusMessage(GsIPCResultType::kWaitingForSimulatorArmed);

// The simulator is now armed.
// The following will cause the waitingForBall state to begin watching for the ball
GolfSimEventElement beginWaitingForSimulatorArmed{ new GolfSimEvent::BeginWaitingForSimulatorArmed{ } };
GolfSimEventQueue::QueueEvent(beginWaitingForSimulatorArmed);

return state::WaitingForBall{ std::chrono::steady_clock::now() };
}


/*********** WaitingForBallHit ************/

Expand Down Expand Up @@ -624,6 +678,9 @@ namespace golf_sim {
[](const state::WaitingForCamera2PreImage& waitingForCamera2PreImage) {
GS_LOG_TRACE_MSG(trace, "WaitingForCamera2PreImage.");
},
[](const state::WaitingForSimulatorArmed& waitingForSimulatorArmed) {
GS_LOG_TRACE_MSG(trace, "WaitingForSimulatorArmed.");
},
[](const state::InitializingCamera2System& initializingCamera1System) {
GS_LOG_TRACE_MSG(trace, "InitializingCamera2System.");
},
Expand Down
Loading
Loading