1 Commits

Author SHA1 Message Date
Michael Mikovsky 99c3fa48dc Add block mean binarization 2025-12-06 11:45:23 -07:00
22 changed files with 139 additions and 43 deletions
Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -112,7 +112,7 @@ public class DropdownType extends FieldType {
.layout_match_wrap()
.padding(20)
.size(18)
.align_left()
.align_center()
.build());
}
@@ -121,7 +121,7 @@ public class NumberType extends FieldType {
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap()
.align_left()
.align_center()
.size(24)
.build());
}
@@ -106,7 +106,7 @@ public class TallyType extends FieldType {
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap()
.align_left()
.align_center()
.size(24)
.build());
}
@@ -115,7 +115,7 @@ public class TextType extends FieldType {
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), (String) data.get())
.layout_match_wrap()
.align_left()
.align_center()
.size(18)
.build());
}
@@ -11,7 +11,6 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
@@ -222,18 +221,19 @@ public class TeamsFragment extends Fragment {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues);
TextView title = new TextViewBuilder(getContext(),
"M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
.align_center()
.size(30)
.padding(0, 0, 40, 5)
.build();
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
binding.matchArea.addView(title);
binding.matchArea.addView(
new TextViewBuilder(getContext(), "M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
.align_center()
.size(30)
.padding(0,0,40,5)
.build()
);
for (int i = 0; i < psda.data.array.length; i++) {
TextViewBuilder tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_left()
.align_center()
.size(25);
if (psda.data.array[i].isNull()) {
@@ -282,7 +282,6 @@ public class TeamsFragment extends Fragment {
.build()
);
if(data[i] != null)
match_latest_values[i].add_compiled_view(binding.matchArea, data[i]);
}
@@ -308,13 +307,13 @@ public class TeamsFragment extends Fragment {
for(int i = 0; i < match_latest_values.length; i++){
TextView tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
binding.matchArea.addView(
new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.size(30)
.padding(0,0,20,5)
.build();
tv.setPaintFlags(tv.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
binding.matchArea.addView(tv);
.build()
);
if(data[i] != null)
match_latest_values[i].add_history_view(binding.matchArea, data[i]);
@@ -152,8 +152,6 @@ public class SettingsFragment extends Fragment {
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
manager.addItem(new DropdownSettingsItem(FieldImageKey, "Field Image", new String[]{
"2026",
"2026 (Flipped)",
"2025",
"2025 (Flipped)"
}));
@@ -87,6 +87,10 @@ public class CodeScannerView extends Fragment {
);
}
}
private static final int BLOCK_SIZE = 32; // Size of each block in pixels
private Bitmap toGreyscale(Image image){
// Turns out the "Y" In YUV is the Luminance of the pixel.
// Makes converting to greyscale 1000x easier
@@ -94,12 +98,17 @@ public class CodeScannerView extends Fragment {
final int width = image.getWidth();
final int height = image.getHeight();
int[] pixels = new int[width * height];
for (int i = 0; i < width*height; i++) {
int L = levelMap[yBuffer.get(i) & 0xff];
// int L = levelMap[yBuffer.get(i) & 0xff];
int L = yBuffer.get(i) & 0xff;
pixels[i] = 0xff000000 | (L << 16) | (L << 8) | L;
}
threshold(pixels, width, height);
Matrix matrix = new Matrix();
matrix.postRotate(90);
@@ -112,6 +121,110 @@ public class CodeScannerView extends Fragment {
return bitmap;
}
/**
* Performs mean block binarization.
* * Note: The function name 'toGreyscale' is kept per your request,
* but this method actually performs binarization (Black/White).
*
* @param image The array of pixels (ARGB format, standard in Android)
* @param width The width of the image
* @param height The height of the image
*/
private void threshold(int[] image, int width, int height) {
// 1. Setup Block Grid Dimensions
// We use Math.ceil to ensure partial blocks at the edges are counted
int gridCols = (int) Math.ceil((double) width / BLOCK_SIZE);
int gridRows = (int) Math.ceil((double) height / BLOCK_SIZE);
// Arrays to store statistics for each block
int[] blockSums = new int[gridCols * gridRows];
int[] blockCounts = new int[gridCols * gridRows];
int[] blockMeans = new int[gridCols * gridRows];
// --- PASS 1: Compute Block Statistics (Mean Intensity) ---
for (int y = 0; y < height; y++) {
int blockY = y / BLOCK_SIZE;
for (int x = 0; x < width; x++) {
int blockX = x / BLOCK_SIZE;
int blockIndex = blockY * gridCols + blockX;
// Extract average grayscale value from ARGB pixel
int pixel = image[y * width + x];
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
// Simple average (matches BoofCV's typical approach for generic buffers)
int gray = (r + g + b) / 3;
blockSums[blockIndex] += gray;
blockCounts[blockIndex]++;
}
}
// Calculate the mean for every block
for (int i = 0; i < blockSums.length; i++) {
if (blockCounts[i] > 0) {
blockMeans[i] = blockSums[i] / blockCounts[i];
}
}
// --- PASS 2: Apply Threshold using Local 3x3 Block Region ---
for (int blockY = 0; blockY < gridRows; blockY++) {
for (int blockX = 0; blockX < gridCols; blockX++) {
// Calculate the threshold for this specific block
// by averaging the means of the surrounding 3x3 blocks.
// This corresponds to BoofCV's `thresholdFromLocalBlocks` logic.
long localSum = 0;
int localCount = 0;
int startGridY = Math.max(0, blockY - 1);
int endGridY = Math.min(gridRows - 1, blockY + 1);
int startGridX = Math.max(0, blockX - 1);
int endGridX = Math.min(gridCols - 1, blockX + 1);
for (int ny = startGridY; ny <= endGridY; ny++) {
for (int nx = startGridX; nx <= endGridX; nx++) {
localSum += blockMeans[ny * gridCols + nx];
localCount++;
}
}
int threshold = (localCount > 0) ? (int) (localSum / localCount) : 127;
// Apply this threshold to all pixels within the current block
int startPixelX = blockX * BLOCK_SIZE;
int startPixelY = blockY * BLOCK_SIZE;
// Handle image boundary (if image size isn't perfectly divisible by block size)
int endPixelX = Math.min(startPixelX + BLOCK_SIZE, width);
int endPixelY = Math.min(startPixelY + BLOCK_SIZE, height);
for (int y = startPixelY; y < endPixelY; y++) {
for (int x = startPixelX; x < endPixelX; x++) {
int index = y * width + x;
// Recalculate gray to compare against threshold
int pixel = image[index];
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
int gray = (r + g + b) / 3;
// Binarize: Black if <= threshold, White if > threshold
if (gray <= threshold) {
image[index] = 0xFF000000; // Black (Alpha 255)
} else {
image[index] = 0xFFFFFFFF; // White (Alpha 255)
}
}
}
}
}
}
public void scanQRCode(Bitmap bitmap) {
// CodeScanTask async = new CodeScanTask();
@@ -81,12 +81,6 @@ public class FieldPosView extends FrameLayout {
case "2025 (Flipped)":
setImageResource(R.drawable.field_2025_flipped);
break;
case "2026":
setImageResource(R.drawable.field_2026);
break;
case "2026 (Flipped)":
setImageResource(R.drawable.field_2026_flipped);
break;
}
}
@@ -46,8 +46,8 @@ public class SettingsManager {
hm.put(UnameKey, "Username");
hm.put(SelEVCodeKey, "unset");
hm.put(WifiModeKey, false);
hm.put(YearNumKey, 2026);
hm.put(FieldImageKey, "2026");
hm.put(YearNumKey, 2025);
hm.put(FieldImageKey, "2025");
hm.put(MatchNumKey, 0);
hm.put(AllyPosKey, "red-1");
hm.put(DataModeKey, 0);
BIN
View File
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 KiB

@@ -60,7 +60,7 @@
android:layout_height="wrap_content"
android:text="Pit Data"
android:textAlignment="center"
android:textSize="35sp"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
@@ -73,11 +73,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/divider3"
android:layout_width="match_parent"
android:layout_height="12dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
<TextView
@@ -86,7 +81,7 @@
android:layout_height="wrap_content"
android:text="Match Data"
android:textAlignment="center"
android:textSize="35sp"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
@@ -144,13 +139,9 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/divider2"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
</LinearLayout>
</ScrollView>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions]
agp = "8.13.2"
agp = "8.13.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1
View File
@@ -17,6 +17,7 @@ dependencyResolutionManagement {
google()
mavenCentral()
maven ( url = "https://jitpack.io" )
jcenter()
}
}