Skip to content

Commit b2940ab

Browse files
committed
turkish overlay roi selection
1 parent 3550051 commit b2940ab

File tree

7 files changed

+195
-285
lines changed

7 files changed

+195
-285
lines changed

foreign/build/CMakeFiles/CMakeConfigureLog.yaml

Lines changed: 97 additions & 97 deletions
Large diffs are not rendered by default.

foreign/main.cpp

Lines changed: 82 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ struct VideoROI {
9090
float y;
9191
float width;
9292
float height;
93+
94+
void fromRect(cv::Rect rect, float scaleX, float scaleY)
95+
{
96+
x = rect.x / scaleX;
97+
y = rect.y / scaleY;
98+
width = rect.width / scaleX;
99+
height = rect.height / scaleY;
100+
}
93101
};
94102

95103
std::ostream& operator<<(std::ostream& os, const VideoROI& obj) {
@@ -276,6 +284,7 @@ int classify_digit(const cv::Ptr<cv::ml::SVM>& svm, const cv::Mat& region)
276284
resized.copyTo(centeredMat(cv::Rect(x_offset, y_offset, width, height)));
277285
}
278286
catch (const std::exception& e) {
287+
std::cout << "Trying to resize with rect " << cv::Rect(x_offset, y_offset, width, height) << std::endl;
279288
std::cerr << "Error in digit classification: " << e.what() << std::endl;
280289
return -1;
281290
}
@@ -315,7 +324,7 @@ int8_t classify_score(const cv::Ptr<cv::ml::SVM>& svm, cv::Mat& region, OverlayC
315324
}
316325
else if (overlay.id == 3)
317326
{
318-
cv::threshold(gray, thresholded, 130, 255, cv::THRESH_BINARY_INV);
327+
cv::threshold(gray, thresholded, 135, 255, cv::THRESH_BINARY_INV);
319328
}
320329

321330
cv::Mat left_mat = thresholded(cv::Rect(0, 0, thresholded.cols / 2, thresholded.rows));
@@ -401,184 +410,6 @@ cv::Ptr<cv::ml::SVM> preload_digit_model(const std::string& svm_path)
401410
return svm;
402411
}
403412

404-
VideoAnalysis* process(const char* video_path, OverlayConfig overlay)
405-
{
406-
VideoAnalysis* analysis = new VideoAnalysis();
407-
408-
cv::Ptr<cv::ml::SVM> svm = preload_digit_model("./foreign/svm_model.xml");
409-
410-
int SKIP_RATE = 20;
411-
int SCORE_CHECK_DELAY = 450;
412-
413-
cv::VideoCapture cap(video_path, cv::CAP_FFMPEG);
414-
cap.set(cv::CAP_PROP_HW_ACCELERATION, cv::VIDEO_ACCELERATION_ANY);
415-
if (!cap.isOpened()) return 0;
416-
417-
double fps = cap.get(cv::CAP_PROP_FPS);
418-
int width = cap.get(cv::CAP_PROP_FRAME_WIDTH);
419-
int height = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
420-
int frame_count = cap.get(cv::CAP_PROP_FRAME_COUNT);
421-
422-
std::cout << "Selected overlay: " << overlay << std::endl;
423-
424-
std::cout << "FPS: " << fps << "\n"
425-
<< "Width: " << width << "\n"
426-
<< "Height: " << height << "\n"
427-
<< "Total Frames: " << frame_count << std::endl;
428-
429-
cv::Rect redRoi = roiFromVideoInfo(width, height, overlay.red);
430-
cv::Rect greenRoi = roiFromVideoInfo(width, height, overlay.green);
431-
cv::Rect redScoreRoi = roiFromVideoInfo(width, height, overlay.red_score);
432-
cv::Rect greenScoreRoi = roiFromVideoInfo(width, height, overlay.green_score);
433-
cv::Mat frame;
434-
cv::Ptr<cv::Tracker> tracker = cv::TrackerMIL::create();
435-
436-
cap >> frame;
437-
438-
while (redRoi.width == 0 || redRoi.height == 0)
439-
{
440-
// cv::rectangle(frame, redRoi, cv::Scalar(0, 255, 0), 2);
441-
imshow("tracker", frame);
442-
redRoi = selectROI("tracker", frame);
443-
std::cout << "Red ROI: " << redRoi << std::endl;
444-
}
445-
while (greenRoi.width == 0 || greenRoi.height == 0)
446-
{
447-
greenRoi = selectROI("tracker", frame);
448-
std::cout << "Green ROI: " << greenRoi << std::endl;
449-
}
450-
while (redScoreRoi.width == 0 || redScoreRoi.height == 0)
451-
{
452-
redScoreRoi = selectROI("tracker", frame);
453-
std::cout << "Red score ROI: " << redScoreRoi << std::endl;
454-
}
455-
while (greenScoreRoi.width == 0 || greenScoreRoi.height == 0)
456-
{
457-
greenScoreRoi = selectROI("tracker", frame);
458-
std::cout << "Green score ROI: " << greenScoreRoi << std::endl;
459-
}
460-
461-
bool redOn = false;
462-
bool greenOn = false;
463-
464-
int doubleTimeout = 0;
465-
int frames_to_skip = SKIP_RATE;
466-
bool should_check_score = false; // Should we check the score?
467-
uint32_t delay_check_frame = -1; // Another frame far in the future just in case there was video on 14-14
468-
469-
uint8_t last_red = -1;
470-
uint8_t last_green = -1;
471-
472-
for (int i = 1; i < frame_count; i++) {
473-
int seconds = i / 30;
474-
cap >> frame;
475-
if (should_check_score || i == delay_check_frame)
476-
{
477-
if (i == delay_check_frame) std::cout << "Delay check at " << delay_check_frame << std::endl;
478-
cv::Mat redScoreMask = frame(redScoreRoi);
479-
cv::Mat greenScoreMask = frame(greenScoreRoi);
480-
481-
uint8_t redScore = classify_score(svm, redScoreMask, overlay);
482-
uint8_t greenScore = classify_score(svm, greenScoreMask, overlay);
483-
std::cout << "[ANALYSIS] Score at frame " << i << ": " << static_cast<int>(redScore) << "-" << static_cast<int>(greenScore) << std::endl;
484-
// If the score is the same as before, the last touch must have been simul or annuled, so we overwrite
485-
// if (redScore == last_red && greenScore == last_green)
486-
// {
487-
// analysis->touch_count -= 1;
488-
// // If i is the delay check frame, it's not the actual time when it happened
489-
// if (should_check_score)
490-
// {
491-
// analysis->touches[analysis->touch_count].frame = i;
492-
// }
493-
// }
494-
// else
495-
// {
496-
analysis->touches[analysis->touch_count].frame = i;
497-
// }
498-
// Update last score accordingly
499-
if (analysis->touch_count > 0)
500-
{
501-
analysis->touches[analysis->touch_count - 1].score1 = redScore;
502-
analysis->touches[analysis->touch_count - 1].score2 = greenScore;
503-
}
504-
analysis->touch_count++;
505-
last_red = redScore;
506-
last_green = greenScore;
507-
should_check_score = false;
508-
}
509-
if (frames_to_skip > 0)
510-
{
511-
frames_to_skip--;
512-
continue;
513-
}
514-
cv::Mat hsvFrame;
515-
cv::cvtColor(frame, hsvFrame, cv::COLOR_RGB2HSV);
516-
cv::Mat redMat = hsvFrame(redRoi);
517-
cv::Mat greenMat = hsvFrame(greenRoi);
518-
519-
cv::Mat redMask;
520-
cv::inRange(redMat, cv::Scalar(120, 100, 160), cv::Scalar(140, 255, 255), redMask);
521-
int redThreshold = redRoi.area() / 3;
522-
int redPixels = cv::countNonZero(redMask);
523-
524-
if (i > 2900 && i < 3000) std::cout << "Red " << redPixels << ", threshold: " << redThreshold << std::endl;
525-
526-
if (redPixels >= redThreshold && !redOn)
527-
{
528-
std::cout << "Red light at " << i << std::endl;
529-
should_check_score = true;
530-
// delay_check_frame = MIN(i + SCORE_CHECK_DELAY, frame_count - 3);
531-
redOn = true;
532-
}
533-
if (redPixels < redThreshold / 3 && redOn)
534-
{
535-
redOn = false;
536-
doubleTimeout = i + 50;
537-
}
538-
539-
cv::Mat greenMask;
540-
cv::inRange(greenMat, cv::Scalar(50, 100, 160), cv::Scalar(70, 255, 255), greenMask);
541-
int greenThreshold = redRoi.area() / 2;
542-
int greenPixels = cv::countNonZero(greenMask);
543-
// std::cout << "Green pixels: " << greenPixels << std::endl;
544-
if (greenPixels > greenThreshold && !greenOn)
545-
{
546-
// std::cout << "Green light at " << i << std::endl;
547-
should_check_score = true;
548-
// delay_check_frame = MIN(i + SCORE_CHECK_DELAY, frame_count - 3);
549-
greenOn = true;
550-
}
551-
if (greenPixels < greenThreshold / 2 && greenOn)
552-
{
553-
greenOn = false;
554-
doubleTimeout = i + 50;
555-
}
556-
557-
if (!redOn && !greenOn)
558-
{
559-
frames_to_skip = SKIP_RATE;
560-
}
561-
}
562-
563-
if (analysis->touch_count > 0) analysis->touch_count--;
564-
565-
return analysis;
566-
}
567-
568-
VideoAnalysis* find_video_touches(const char* video_path, uint8_t overlay_id)
569-
{
570-
try
571-
{
572-
auto result = process(video_path, OVERLAYS[overlay_id]);
573-
return result;
574-
}
575-
catch (const std::exception& e)
576-
{
577-
std::cerr << "Caught exception: " << e.what() << std::endl;
578-
}
579-
return 0;
580-
}
581-
582413
void js_memcpy(void* dest, void* source, size_t size) {
583414
memcpy(dest, source, size);
584415
}
@@ -851,6 +682,55 @@ extern "C" StreamAnalysis* cut_stream(const std::string& tesseract_path, const s
851682
int frame_count = cap.get(cv::CAP_PROP_FRAME_COUNT);
852683
OverlayConfig overlay = OVERLAYS[overlay_id];
853684

685+
cv::Mat frame;
686+
687+
if (overlay_id == 3)
688+
{
689+
cap.read(frame);
690+
cap.set(cv::CAP_PROP_POS_FRAMES, 0);
691+
while (true) {
692+
cv::Mat drawingFrame = frame.clone();
693+
694+
cv::Rect overlayRect = cv::selectROI("Select the overlay region", drawingFrame);
695+
int x = overlayRect.x;
696+
int y = 0;
697+
int width = drawingFrame.cols - overlayRect.x - 1;
698+
int height = overlayRect.y + overlayRect.height;
699+
700+
cv::Rect redNameRect(overlayRect.x, height * 0.02, width / 2, height * 0.2);
701+
cv::Rect greenNameRect(overlayRect.x + width / 2, height * 0.02, width / 2, height * 0.2);
702+
cv::rectangle(drawingFrame, redNameRect, cv::Scalar(0, 0, 255), 2);
703+
cv::rectangle(drawingFrame, greenNameRect, cv::Scalar(0, 255, 0), 2);
704+
705+
cv::Rect timeRect(overlayRect.x + width * 0.4, height * 0.33, width * 0.2, height * 0.2);
706+
cv::rectangle(drawingFrame, timeRect, cv::Scalar(255, 0, 0), 2);
707+
708+
cv::Rect redScoreRect(overlayRect.x + width * 0.275, height * 0.78, width * 0.1, height * 0.18);
709+
cv::Rect greenScoreRect(overlayRect.x + width * 0.635, height * 0.78, width * 0.1, height * 0.18);
710+
cv::rectangle(drawingFrame, redScoreRect, cv::Scalar(0, 0, 255), 2);
711+
cv::rectangle(drawingFrame, greenScoreRect, cv::Scalar(0, 255, 0), 2);
712+
imshow("Press BACKSPACE if the overlay does not line up, otherwise press ENTER", drawingFrame);
713+
int key = cv::waitKey(5000);
714+
std::cout << "Got key: " << key << std::endl;
715+
if (key == 127 || key == 8) // Backspace
716+
{
717+
cv::destroyWindow("Select the overlay region");
718+
cv::destroyWindow("Press BACKSPACE if the overlay does not line up, otherwise press ENTER");
719+
continue;
720+
}
721+
722+
overlay.red_name.fromRect(redNameRect, frame.cols, frame.rows);
723+
overlay.green_name.fromRect(greenNameRect, frame.cols, frame.rows);
724+
overlay.time.fromRect(timeRect, frame.cols, frame.rows);
725+
overlay.red_score.fromRect(redScoreRect, frame.cols, frame.rows);
726+
overlay.green_score.fromRect(greenScoreRect, frame.cols, frame.rows);
727+
728+
cv::destroyWindow("Select the overlay region");
729+
cv::destroyWindow("Press BACKSPACE if the overlay does not line up, otherwise press ENTER");
730+
break;
731+
}
732+
}
733+
854734
analysis->fps = fps;
855735
analysis->frame_count = frame_count;
856736

@@ -871,8 +751,6 @@ extern "C" StreamAnalysis* cut_stream(const std::string& tesseract_path, const s
871751
cv::Rect timeRoi = roiFromVideoInfo(width, height, overlay.time);
872752
cv::Rect tableauRoi = roiFromVideoInfo(width, height, overlay.tableau);
873753

874-
cv::Mat frame;
875-
876754
int skip_rate = SKIP_SECONDS * fps;
877755
int min_bout_length = MIN_BOUT_SECONDS * fps;
878756

@@ -901,7 +779,7 @@ extern "C" StreamAnalysis* cut_stream(const std::string& tesseract_path, const s
901779
}
902780
bool score_nonzero = redScore > 0 || greenScore > 0;
903781

904-
// std::cout << "Score at frame " << i << " (" << i / fps << "s, " << i * 100 / frame_count << "%): " << (int)redScore << "-" << (int)greenScore << std::endl;
782+
std::cout << "Score at frame " << i << " (" << i / fps << "s, " << i * 100 / frame_count << "%): " << (int)redScore << "-" << (int)greenScore << std::endl;
905783

906784
if (!bout_running && score_nonzero)
907785
{
@@ -916,6 +794,12 @@ extern "C" StreamAnalysis* cut_stream(const std::string& tesseract_path, const s
916794
cap.set(cv::CAP_PROP_POS_FRAMES, i - rewind_amount * skip_rate);
917795
cap.read(local_frame);
918796

797+
cv::Mat redScoreMask = frame(redScoreRoi);
798+
cv::Mat greenScoreMask = frame(greenScoreRoi);
799+
int8_t redScore = classify_score(svm, redScoreMask, overlay);
800+
int8_t greenScore = classify_score(svm, greenScoreMask, overlay);
801+
if (redScore > 0 || greenScore > 0) continue;
802+
919803
cv::Mat timeMat = local_frame(timeRoi);
920804
cv::cvtColor(timeMat, timeMat, cv::COLOR_BGR2GRAY);
921805
tess.SetImage(timeMat.data, timeMat.cols, timeMat.rows, 1, timeMat.step); // Feed binary image to Tesseract
@@ -1004,14 +888,30 @@ extern "C" StreamAnalysis* cut_stream(const std::string& tesseract_path, const s
1004888
}
1005889
if (bout_running && !score_nonzero)
1006890
{
891+
// cv::Mat local_frame;
892+
// cap.set(cv::CAP_PROP_POS_FRAMES, i + skip_rate);
893+
// cap.read(local_frame);
894+
895+
// cv::Mat redScoreMask = local_frame(redScoreRoi);
896+
// cv::Mat greenScoreMask = local_frame(greenScoreRoi);
897+
// int8_t redScore = classify_score(svm, redScoreMask, overlay);
898+
// int8_t greenScore = classify_score(svm, greenScoreMask, overlay);
899+
// if (redScore > 0 || greenScore > 0)
900+
// {
901+
// std::cout << "Score hit 0 at frame " << i << ", but score restored immediately after" << std::endl;
902+
// }
903+
// else
904+
// {
1007905
std::cout << "Bout ended at frame " << i << " (" << i / fps << "s, " << i * 100 / frame_count << "%)!" << std::endl;
1008906
bout_running = false;
1009907
if (i - analysis->bouts[analysis->bout_count].start_frame > min_bout_length)
1010908
{
1011909
analysis->bouts[analysis->bout_count].end_frame = i;
1012910
analysis->bout_count += 1;
1013911
}
912+
// }
1014913
}
914+
// cap.set(cv::CAP_PROP_POS_FRAMES, i);
1015915

1016916
frames_to_skip = skip_rate;
1017917
}

lib/ffi.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'dart:ffi';
22
import 'dart:io';
33

44
import 'package:ffi/ffi.dart';
5-
import 'package:path/path.dart' as path;
65

76
base class StreamBoutSegment extends Struct {
87
@Int32()

lib/main.dart

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'dart:ffi';
22
import 'dart:io';
3-
import 'dart:isolate';
43

54
import 'package:desktop_drop/desktop_drop.dart';
65
import 'package:ffi/ffi.dart';
@@ -162,7 +161,7 @@ class _MainAppState extends State<MainApp> {
162161
final outputFolderPtr = outputFolder!.toNativeUtf8();
163162
final eventNamePtr = _controller.text.trim().toNativeUtf8();
164163

165-
final resultPtr = ffi.cutStream(
164+
ffi.cutStream(
166165
tessdataPtr,
167166
svmModelPtr,
168167
selectedFilePtr,
@@ -220,11 +219,23 @@ class _MainAppState extends State<MainApp> {
220219
),
221220
body: DropTarget(
222221
onDragDone: (detail) {
223-
const videoExtensions = {'.mp4', '.mov', '.avi', '.mkv', '.webm', '.flv', '.wmv', '.mpeg', '.3gp'};
224-
if(!videoExtensions.any((ext) => detail.files[0].path.toLowerCase().endsWith(ext))) {
222+
const videoExtensions = {
223+
'.mp4',
224+
'.mov',
225+
'.avi',
226+
'.mkv',
227+
'.webm',
228+
'.flv',
229+
'.wmv',
230+
'.mpeg',
231+
'.3gp'
232+
};
233+
if (!videoExtensions.any(
234+
(ext) => detail.files[0].path.toLowerCase().endsWith(ext))) {
225235
ScaffoldMessenger.of(context).hideCurrentSnackBar();
226236
ScaffoldMessenger.of(context).showSnackBar(
227-
SnackBar(content: Text('Error: Dragged file was not a video.')),
237+
SnackBar(
238+
content: Text('Error: Dragged file was not a video.')),
228239
);
229240
} else {
230241
selectedFile = detail.files[0].path;
-1.5 KB
Binary file not shown.
-847 Bytes
Binary file not shown.
-1.38 KB
Binary file not shown.

0 commit comments

Comments
 (0)