Advertisement
PolyCreativity

Convolution Matrix In Google Sheets (AppScripts)

Nov 24th, 2023
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 6.09 KB | Source Code | 0 0
  1. /* -------------------- Data -------------------- */
  2. const sobelLeftEdge = [
  3.   [-1,0,1],
  4.   [-2,0,2],
  5.   [-1,0,1],
  6. ]
  7. const sobelRightEdge = [
  8.   [1,0,-1],
  9.   [2,0,-2],
  10.   [1,0,-1],
  11. ]
  12. const sobelBottomEdge = [
  13.   [1,2,1],
  14.   [0,0,0],
  15.   [-1,-2,-1],
  16. ]
  17. const sobelTopEdge = [
  18.   [-1,-2,-1],
  19.   [0,0,0],
  20.   [1,2,1],
  21. ]
  22.  
  23. const blurMatrix = [
  24.   [1/9,1/9,1/9],
  25.   [1/9,1/9,1/9],
  26.   [1/9,1/9,1/9]
  27. ]
  28.  
  29. const outlineMatrix = [
  30.   [-1,-1,-1],
  31.   [-1,8,-1],
  32.   [-1,-1,-1],
  33. ]
  34.  
  35. const sharpenMatrix = [
  36.   [-1,-1,-1],
  37.   [-1,9,-1],
  38.   [-1,-1,-1],
  39. ]
  40.  
  41. /*-------------------- Google Docs UI Callbacks -------------------- */
  42.  
  43. function onOpen() {
  44.   var ui = SpreadsheetApp.getUi();
  45.  
  46.   //Create the custom menu
  47.   ui.createMenu('Convolutions')
  48.       .addSubMenu(
  49.           ui.createMenu('Sobel')
  50.           .addItem('Left edge','sobelLeft')
  51.           .addItem('Right edge','sobelRight')
  52.           .addItem('Top edge','sobelTop')
  53.           .addItem('Bottom edge','sobelBottom')
  54.       )
  55.       .addSubMenu(
  56.           ui.createMenu("Effects")
  57.           .addItem('Blur','doBlur')
  58.           .addItem('Sharpen','doSharpen')
  59.           .addItem('Outline','doOutline')
  60.  
  61.       )
  62.       .addItem('Highlight','applyHighlight')
  63.       .addToUi()
  64. }
  65.  
  66.  
  67. /*------------------- Menu Callbacks -------------------- */
  68.  
  69. function sobelRight(){
  70.   applyMatrix(sobelRightEdge)
  71.  
  72. }
  73.  
  74. function sobelTop(){
  75.   applyMatrix(sobelTopEdge)
  76.  
  77. }
  78.  
  79. function sobelLeft(){
  80.   applyMatrix(sobelLeftEdge)
  81.  
  82. }
  83.  
  84. function sobelBottom(){
  85.   applyMatrix(sobelBottomEdge)
  86.  
  87. }
  88.  
  89. function doSharpen(){
  90.  
  91. let activeRange = SpreadsheetApp.getActiveSheet().getSelection().getActiveRange()
  92.  
  93.   let reply = SpreadsheetApp.getUi().prompt("Amount (0%-100%)")
  94.   let amount = parseFloat(reply.getResponseText())
  95.   amount = (amount/100)
  96.   //--------------------
  97.   //Perform the math
  98.   //--------------------
  99.   let grid = activeRange.getValues()  
  100.   sharpMask = convolute(grid,sharpenMatrix)
  101.   for( y = 0; y < grid.length; y++ ){
  102.     for ( x = 0; x < grid[y].length; x++ ){
  103.       grid[y][x] += sharpMask[y][x] * amount
  104.     }
  105.   }
  106.   grid = clamp(grid,0,255)
  107.   grid = quantize(grid)
  108.  
  109.   //--------------------
  110.   //Apply the grid
  111.   //--------------------
  112.   activeRange.setValues(grid)
  113.   highlightRange(activeRange)
  114.  
  115. }
  116.  
  117. function doOutline(){
  118.   applyMatrix(outlineMatrix)
  119.  
  120. }
  121. function doBlur(){
  122.   applyMatrix(blurMatrix)
  123. }
  124.  
  125. /*-------------------- Spreadsheet functions -------------------- */
  126. function applyMatrix(matrix){
  127.  
  128.   let activeRange = SpreadsheetApp.getActiveSheet().getSelection().getActiveRange()
  129.  
  130.   //--------------------
  131.   //Perform the math
  132.   //--------------------
  133.   let grid = activeRange.getValues()  
  134.   grid = convolute(grid,matrix)
  135.   grid = clamp(grid,0,255)
  136.   grid = quantize(grid)
  137.  
  138.   //--------------------
  139.   //Apply the grid
  140.   //--------------------
  141.   activeRange.setValues(grid)
  142.   highlightRange(activeRange)
  143.  
  144. }
  145.  
  146. function applyHighlight(){
  147.   activeRange = SpreadsheetApp.getActiveSheet().getSelection().getActiveRange()
  148.   highlightRange(activeRange)
  149. }
  150. function highlightRange(activeRange){
  151.   let grid = activeRange.getValues()
  152.   let color = []
  153.  
  154.   for( y = 0; y < grid.length; y++ ){
  155.     color[y] = []
  156.     for ( x = 0; x < grid[y].length; x++ ){
  157.       let i = Math.max(0,Math.min(255,grid[y][x]))
  158.       color[y][x] = `rgb(${i},${i},${i})`
  159.  
  160.     }
  161.   }
  162.   activeRange.setBackgrounds(color)
  163.  
  164. }
  165.  
  166.  
  167. /*-------------------- Math -------------------- */
  168.  
  169.  
  170. //Clamp every item in a grid
  171. function clamp(grid,lowest,highest){
  172.   for (y = 0; y < grid.length ; y++ ){
  173.     for ( x = 0; x < grid[y].length ; x++ ){
  174.       grid[y][x] = Math.max(lowest,Math.min(highest,grid[y][x]))
  175.     }
  176.   }
  177.   return grid
  178. }
  179.  
  180. //Floor every item in a grid
  181. function quantize(grid){
  182.   for (y = 0; y < grid.length ; y++ ){
  183.     for ( x = 0; x < grid[y].length ; x++ ){
  184.       grid[y][x] = Math.floor(grid[y][x])
  185.     }
  186.   }
  187.   return grid
  188. }
  189.  
  190. //Apply a convolution matrix to a grid
  191. function convolute(inputGrid,matrix) {
  192.  
  193.   let gridHeight = inputGrid.length
  194.   if ( gridHeight <= 1 ) throw "Selected items must be larger than 1 tall"
  195.  
  196.   let gridWidth = inputGrid[0].length
  197.   if ( gridWidth <= 1 ) throw "Selected items must be larger than 1 wide"
  198.  
  199.   let matrixHeight = matrix.length
  200.   if ( matrixHeight <= 1 ) throw "Matrix must be at least 1 cell tall"
  201.   if ( matrixHeight%2 == 0 ) throw "Matrix height must be an odd number of cells"
  202.  
  203.   let matrixWidth = matrix[0].length
  204.   if ( matrixWidth <= 1 ) throw "Matrix must be at least 1 cell width"
  205.   if ( matrixWidth%2 == 0 ) throw "Matrix width must be an odd number of cells"
  206.  
  207.  
  208.   //Calculate temporary values for working with the matrix
  209.  
  210.   let halfMatrixHeight = Math.floor(matrixHeight/2)
  211.   let halfMatrixWidth = Math.floor(matrixWidth/2)
  212.  
  213.  
  214.   //Create a new grid to return so our values don't influence
  215.   //eachother during the sweep
  216.   let newGrid = []
  217.   for ( gridY = 0; gridY < gridHeight; gridY++ ){
  218.     newGrid[gridY] = []
  219.     for ( gridX = 0; gridX < gridWidth; gridX++ ){
  220.       newGrid[gridY][gridX] = 0      
  221.     }
  222.      
  223.   }
  224.  
  225.   //for every item in the grid
  226.   for ( gridY = 1; gridY < gridHeight - 1; gridY++ ){
  227.  
  228.     for ( gridX = 1; gridX < gridWidth - 1; gridX++ ){
  229.      
  230.       //We'll sum up the values we sampled from nearby cells
  231.       let sum = 0
  232.      
  233.       //for every item in the matrix
  234.       for ( matrixY = 0; matrixY < matrixHeight; matrixY++ ){
  235.         for ( matrixX = 0; matrixX < matrixWidth; matrixX++ ){            
  236.  
  237.           //calculate the offset in the main grid
  238.           let offsetY = matrixY - halfMatrixHeight
  239.           let offsetX = matrixX - halfMatrixWidth
  240.  
  241.           //grab the value from the INPUT grid + offset
  242.           let gridValue = inputGrid[gridY+offsetY][gridX+offsetX]
  243.  
  244.           //grab the value from the matrix
  245.           let matrixValue = matrix[matrixY][matrixX]
  246.  
  247.           //multiply the grid value * matrix value
  248.           //and add to the sum
  249.           sum += ( gridValue *  matrixValue )
  250.         }
  251.       }
  252.       //set the NEW grid item
  253.       newGrid[gridY][gridX] = sum
  254.     }
  255.   }
  256.   return newGrid
  257. }
  258.  
  259.    
  260.  
  261.  
  262.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement