3  Board mapping

Author

Eric Wanjau and Ian Muchiri

This notebook illustrates how we assigned real world board coordinates (cm) to each square box on the chess board.

The approach was quite straightforward. We created a virtual board and then translated virtual coordinates to the manipulator’s workspace.

3.1 Creating virtual chessboard.

library(here)
library(waffle)
library(patchwork)
library(magick)
library(tidyverse)

# Vector
x <- c(A= 1, B = 1, C = 1, D = 1, E = 1, F = 1, G = 1)
y <- c(A= 1, B = 1, C = 1, D = 1, E = 1, F = 1, G = 1)

# Create checker boxes
w1 = waffle(x, rows = 8, flip = TRUE, colors = c("black", "white", "black", "white", "black", "white", "black", "white"), legend_pos = "", , size = 0.1) + 
  theme(plot.margin = margin(0, 0, 0, 0))
w2 = waffle(y, rows = 8, flip = TRUE, colors = c("white", "black", "white", "black", "white", "black", "white", "black"), legend_pos = "", size = 0.1) +
  theme(plot.margin = margin(0, 0, 0, 0))

# Make checker board
checkerboard <- w1 / w2 / w1 / w2 / w1 / w2 / w1 / w2
checkerboard

ggsave("images/checkerboard.png", width = 7, height = 7)

3.2 Convert image into a tibble

library(magick)
img <- image_read("images/checkerboard.png") %>% 
  image_convert(type = "grayscale")
# Dimensions of virtual board
dim_x = 160 * 4
dim_y = 160 * 4
img <- image_resize(img, paste(dim_x, dim_y, sep = "x"))


# Create row and column identifiers
row_names <-  tibble(x = 1:dim_x, y = rep(LETTERS[1:8], each = dim_y/8), z = paste(x, "_", y, sep = "")) %>% pull(z)

col_names <-  tibble(x = 1:dim_x, y = rep(1:8, each = dim_x/8), z = paste(x, "_", y, sep = "")) %>% pull(z)

# Create array and number rows and columns
img_array <- drop(as.integer(pluck(img, 1)))
rownames(img_array) <- row_names
colnames(img_array) <- col_names


# Create data frame from array and rename column
img_dfx <- img_array %>% 
  as_tibble() %>% 
  mutate(y = row_names) %>% 
  #rowid_to_column(var = "y") %>% 
  pivot_longer(!y, names_to = "x", values_to = "pv") %>% 
  mutate(pv = scales::rescale(pv, to = c(0, 1))) %>% 
  # binarize image
  mutate(pv = case_when(
    pv > 0.5 ~ 1,
    TRUE ~ 0)) %>% 
  separate(y, c("y", "pl")) %>% 
  separate(x, c("x", "pn")) %>% 
  mutate(pos = paste(pl, pn, sep = "")) %>% 
  select(-c(pn, pl)) %>% 
  mutate(across(c(y, x, pv), as.numeric)) %>% 
  group_by(pos) %>% 
  mutate(centroidx = round(mean(x)), centroidy = round(mean(y))) %>% 
  ungroup()

Only centroid locations are of importance. So we narrow down to that:

# Obtain location centroids
centroids <- img_dfx %>% 
  ungroup() %>% 
  distinct(pos, centroidx, centroidy)

# View centroids on the virtual board
centroids %>% 
  slice_head(n = 10)
# A tibble: 10 x 3
   pos   centroidx centroidy
   <chr>     <dbl>     <dbl>
 1 A1           40        40
 2 A2          120        40
 3 A3          200        40
 4 A4          280        40
 5 A5          360        40
 6 A6          440        40
 7 A7          520        40
 8 A8          600        40
 9 B1           40       120
10 B2          120       120

3.3 Mapping coordinates to xy

Once the centroid pixel coordinates were successfully extracted, the next step involved translating them to their corresponding real-world x-y coordinates in cm. This scaling from pixel values to cm values was achieved by the mapping function or simply using scales::rescale():

# Mapping function
map_fun <- function(value, from_low, from_high, to_low, to_high){
  
  mapped_val = (value - from_low) * (to_high - to_low) / (from_high - from_low) + (to_low)
  
  return(mapped_val)
}


# Map virtual board coordinates to manipulator workspace
centroids = centroids %>% 
    # mutate(x_mapped = map_fun(centroidx, from_low = 0, from_high = dim_x, to_low = 9, to_high = -9),
    #        y_mapped = map_fun(centroidy, from_low = 0, from_high = 192, to_low = 11, to_high = 17)) %>% 
    
    ## Using scales::rescale
    mutate(x_mapped = scales::rescale(centroidx, to = c(9, -9), from = c(0, 640)),
           y_mapped = scales::rescale(centroidy, to = c(11, 17), from = c(0, 192))) %>% 
    mutate(across(where(is.numeric), round)) %>% 
    # Rearrange board positions to match our physical chess board
    mutate(
      pl = rep(LETTERS[1:8], times = nrow(centroids)/8),
      pn = rep(1:8, each = nrow(centroids)/8),
      pos = paste(pl, pn, sep = "")) %>% 
    select(-c(pl, pn))

# View centroids coordinates
centroids %>% 
  slice_head(n = 10)
# A tibble: 10 x 5
   pos   centroidx centroidy x_mapped y_mapped
   <chr>     <dbl>     <dbl>    <dbl>    <dbl>
 1 A1           40        40        8       12
 2 B1          120        40        6       12
 3 C1          200        40        3       12
 4 D1          280        40        1       12
 5 E1          360        40       -1       12
 6 F1          440        40       -3       12
 7 G1          520        40       -6       12
 8 H1          600        40       -8       12
 9 A2           40       120        8       15
10 B2          120       120        6       15

Now let’s visualize our handy work:

theme_set(theme_void())

# Create virtual board
centroids %>% 
  ggplot(mapping = aes(x = centroidx*2, y = centroidy*2)) +
  geom_tile(aes(fill = str_extract(pos, "[:alpha:]")), color = "white", alpha = 0.8, size = 0.8, show.legend = FALSE) +
  geom_text(aes(x = centroidx*2, y = centroidy*2 + 2, label = pos), color = "black", size = 3.5) +
  coord_equal() +
  #paletteer::scale_fill_paletteer_d("RColorBrewer::Set2") +
  scale_fill_manual(values = paletteer::paletteer_d("RColorBrewer::Set2") %>% paste() %>% str_replace("#FFD92FFF", "#FFC000FF"))

# Virtual coordinates on board
centroids %>% 
  ggplot(mapping = aes(x = centroidx*2, y = centroidy*2)) +
  geom_tile(aes(fill = str_extract(pos, "[:alpha:]")), color = "white", alpha = 0.8, size = 0.8, show.legend = FALSE) +
  geom_text(aes(x = centroidx*2, y = centroidy*2 + 2, label = paste("(", centroidx, ",", centroidy, ")", sep = "")), color = "black", size = 3.3) +
  coord_equal() +
  #paletteer::scale_fill_paletteer_d("RColorBrewer::Set2") +
  scale_fill_manual(values = paletteer::paletteer_d("RColorBrewer::Set2") %>% paste() %>% str_replace("#FFD92FFF", "#FFC000FF"))

# Real world coordinates on board
centroids %>% 
  ggplot(mapping = aes(x = centroidx*2, y = centroidy*2)) +
  geom_tile(aes(fill = str_extract(pos, "[:alpha:]")), color = "white", alpha = 0.8, size = 0.8, show.legend = FALSE) +
  geom_text(aes(x = centroidx*2, y = centroidy*2 + 2, label = paste("(", x_mapped, ",", y_mapped, ")", sep = "")), color = "black", size = 3.3) +
  coord_equal() +
  #paletteer::scale_fill_paletteer_d("RColorBrewer::Set2") +
  scale_fill_manual(values = paletteer::paletteer_d("RColorBrewer::Set2") %>% paste() %>% str_replace("#FFD92FFF", "#FFC000FF"))

Here’s an example of how we would extract the real word coordinates from the tibble:

# Function to return mapped values from tibble
get_centroid <- function(position){
  x = centroids %>% 
    filter(str_detect(pos, position)) %>% 
    pull(x_mapped)
  
  y = centroids %>% 
    filter(str_detect(pos, position)) %>% 
    pull(y_mapped)
  
  return(c(x, y))
}

# Define initial and final positions
initial_pos <- "B2"
final_pos <- "G2"
get_centroid(position = initial_pos)
[1]  6 15
get_centroid(position = final_pos)
[1] -6 15

3.4 Summary

This is the approach we took at this step. We created a virtual board and then translated virtual coordinates to the manipulator’s workspace.

And with that, this section is done! Please do feel free to reach out in case of any questions, feedback and suggestions.

Happy Learning,

Eric.