[{"Config":{"clearAuto":true,"strictScope":true,"group":"anonymous","mode":"javascript","file":"workbook.cai","fontSize":14,"thisthat":true,"turbo":true,"user":"liheqoce","docUrl":"localhost:5558","projUrl":"192.168.0.45:5550","webclipTime":300,"webclipUrl":"localhost:9176","webxUrl":"localhost:9177","modules":[],"workdir":"/home/sbosse/proj/workbook","nw":false,"version":"1.8.2","wex":{"http":"http://localhost:11111","ws":"ws://localhost:11112"},"strict":true,"reload":"workbook.cai.pixi"}},{"Notes":[""]},{"Library":"class Data {\n constructor (source,options) {\n this.source=source;\n this.options=options||{};\n this.next=[];\n this.on=options.on?options.on:{};\n this.window=null;\n this.ConsoleId=ConsoleId;\n this.ConsoleOutput=ConsoleOutput;\n Register('Data',this);\n }\n \n get configuration () {\n return {\n sql:Select({\n url:'localhost:9999',\n database:'mydatabase',\n table:'mytable',\n }),\n $template : function (obj,opts,subclass) {\n if (subclass=='sql') return obj+'.start();\\n//'+obj+'.read(\"*\")';\n return '';\n }\n }\n }\n error (op,err) {\n if (this.on.error) {\n this.on.error(op,err)\n }\n }\n async info () {\n var self=this;\n switch (this.source) {\n case 'sql':\n if (this.options.table) {\n return this.db.schema(this.options.table);\n } else if (this.options.database) {\n \n } else {\n \n }\n break;\n }\n }\n // varargs\n async read () {\n var self=this;\n switch (this.source) {\n case 'sql':\n var cols = arguments[0],\n rows = arguments[1],\n single;\n if (typeof cols=='number') { rows='rowid='+(cols+1); cols='*'; single=true };\n if (typeof rows=='number') { rows='rowid='+(rows+1); single=true };\n if (!cols) cols='*';\n \n var data = await this.db.select(this.options.table,cols,rows);\n // Error handling\n if (DB.error(data)) { this.error('sql.read',DB.error(data)); return DB.error(data) };\n if (single) data=data[0];\n if (this.options.filter) this.y=data.map(this.options.filter);\n else this.y=data;\n // Code.print(data)\n self.next.forEach(function (node) {\n node.input(self.y);\n })\n return data;\n break;\n }\n }\n async start () {\n var result='Ok';\n switch (this.source) {\n case 'sql':\n this.db=DB.sqlA(this.options.url);\n if (this.options.database) {\n result = await this.db.open(this.options.database);\n if (DB.error(result)) {\n this.error('sql.open',DB.error(result));\n result=DB.error(result);\n } else\n this._tables=await this.db.tables();\n }\n if (this.options.table) {\n var result=await this.db.schema(this.options.table);\n if (DB.error(result)) {\n this.error('sql.schema',DB.error(result));\n result=DB.error(result);\n } else {\n this._type={}\n for(var i in result) {\n var at = result[i].split(' ');\n this._type[at[0]]=at.slice(1).join(' ');\n }\n result='OK';\n }\n }\n break;\n }\n return result\n }\n output (node) {\n this.next.push(node);\n }\n async rows () {\n var result;\n switch (this.source) {\n case 'sql':\n if (this.options.table) {\n var result = await this.db.count(this.options.table);\n if (DB.error(result)) {\n this.error('sql.count',DB.error(result));\n result=DB.error(result);\n } else {\n result=Number(result)\n }\n }\n return result\n break; \n }\n }\n async type () {\n if (this._type) return this._type;\n }\n}\n\nclass Display {\n constructor(action,options) {\n options=options||{}\n this.options= options;\n this.history=[];\n this.action=action||'info';\n this.window=null;\n this.ConsoleId=ConsoleId;\n this.ConsoleOutput=ConsoleOutput;\n if (options.console) {\n this.id=Utils.uniqueID(16);\n var wrapper = $('
');\n this.options.container=$('#'+this.id);\n this.options.container=wrapper;\n Code.print(wrapper); \n }\n if (options.transpose && options.select && !options.header) options.header=options.select;\n }\n get configuration () {\n return {\n console:Optional(false),\n accumulative:Optional(false),\n depth:Optional(0),\n plot : Select({\n width:Range(100,1200,50,600),\n title:Optional(''),\n 'type line' : Select({\n type:Fixed('line'),\n min:Optional(0),\n max:Optional(0),\n data: '[y]', // dataformat\n _data : ['[y]','{xy}','{xyy}','[[y]]'],\n labels : {\n x:Optional(''),\n y:Optional(''),\n data:Optional([]),\n },\n }),\n 'type bar' : Select({\n type:Fixed('bar'),\n min:Optional(0),\n max:Optional(0),\n data: '[y]', \n _data : ['[y]','{xy}','{xyy}','[[y]]'],\n labels : {\n x:Optional(''),\n y:Optional(''),\n data:Optional([]),\n },\n }),\n 'type image' : Select({\n type:Fixed('image'),\n min:Optional(0),\n max:Optional(0),\n colorIndex:Optional('black'),\n data: 'Matrix', \n _data : ['Matrix','MatrixTA','[[a]]','RGBA','Image'],\n zoom:Optional(1),\n }),\n 'type scatter' : Select({\n type: Fixed('scatter'),\n data: '[{xy}]', \n _data: ['[{xy}]','[[{xy}]]'],\n radius: Range(1,20,1,5),\n labels : {\n x:Optional(''),\n y:Optional(''),\n data:Optional([]),\n },\n }),\n }),\n table : Select({\n size : Range(8,20,1,14),\n header : Optional([]),\n colnames : Optional([]),\n select : Optional([]),\n transpsoe : Optional(false),\n data : '[{a}]',\n _data : ['[[a]]','[{a}]','{a}'],\n }),\n filter:Optional(\"(data) => { return data }\"),\n $finalize : function (options) {\n // options.dataformat=options.data;\n // delete options.data;\n if (options.header) options.header=options.header.split(',');\n if (options.select) options.header=options.select.split(',');\n if (options.colnames) options.header=options.colnames.split(',');\n return options\n },\n $template : function (obj,options,subclass) {\n return '// source.output('+obj+')\\n// '+obj+'.output(target)';\n }\n }\n }\n info (object,verbose) {\n var self=this;\n if (!this.options.console) {\n if (this.window) \n this.window.update(''+typeOf(object,verbose||this.options.verbose)+'');\n else\n this.window=Common.openWin('
'+typeOf(object,verbose||this.options.verbose)+'',{\n title:'Display.info '+(this.options.heading || this.options.title || ''),\n onclosed : function () {\n self.window=null;\n }\n })\n } else {\n print(typeOf(object,verbose||this.verbose))\n }\n }\n log (data) {\n var self=this;\n if (this.options.accumulative) {\n this.history.push(data);\n if (this.options.depth && this.history.length==this.options.depth) \n this.history=this.history.slice(1);\n }\n if (!this.options.console) {\n var content = this.options.accumulative?this.history.map(item => inspect(item)).join('\\n'):\n inspect(data);\n if (this.window) \n this.window.update('
'+content+'');\n else\n this.window=Common.openWin('
'+content+'',{\n title:'Display.log '+(this.options.heading || this.options.title || ''),\n onclosed : function () {\n self.window=null;\n }\n })\n } else {\n print(inspect(data))\n }\n \n }\n plot (data) {\n var self=this;\n if (this.options.accumulative) {\n this.history.push(data);\n if (this.options.depth && this.history.length==this.options.depth) \n this.history=this.history.slice(1);\n }\n var content = this.options.accumulative?this.history:data;\n if (this.options.filter) content=this.options.filter.call(this,content);\n if (!this.options.console) {\n if (this.window) {\n this.options.container.empty();\n Plot(content,self.options);\n } else {\n this.id=Utils.uniqueID(16);\n this.window=Common.openWin('',{\n title:'Display.plot '+(this.options.heading || this.options.title || ''),\n onclosed : function () {\n self.window=null;\n self.options.container=null;\n self.id=null;\n }\n })\n Code.later(1,() => {\n self.options.container=$('#'+self.id);\n this.plotID=Plot(content,self.options);\n })\n }\n } else {\n delete this.options.rois;\n if (this.plotID) this.options.replace=this.plotID;\n this.plotID=Plot(content,this.options)\n } \n }\n table (data) {\n var self=this;\n if (this.options.accumulative) {\n this.history.push(data);\n if (this.options.depth && this.history.length==this.options.depth) \n this.history=this.history.slice(1);\n }\n var content = this.options.accumulative?this.history:data;\n if (this.options.select) {\n switch (this.options.data) {\n case '{a}':\n var _content=[];\n for(var i in self.options.select) {\n var key = self.options.select[i];\n _content.push(content[key]);\n }\n content=_content;\n if (!this.options.transpose && content.length==1) content=content[0];\n break;\n case '[{a}]': \n content=content.map(function (row,rowi) {\n var _row={}\n for(var i in self.options.select) {\n var key = self.options.select[i];\n _row[key]=row[key];\n }\n return _row\n })\n break;\n case '[[a]]': \n content=content.map(function (row,rowi) {\n var _row=[]\n for(var i in self.options.select) {\n var key = self.options.select[i];\n _row.push(row[key]);\n }\n return _row\n })\n break;\n }\n }\n if (this.options.filter) content=this.options.filter.call(this,content);\n if (!this.options.console) {\n if (this.window) {\n this.options.container.empty();\n this.tableID=Table(content,self.options);\n } else {\n this.id=Utils.uniqueID(16);\n this.window=Common.openWin('',{\n title:'Display.table '+(this.options.heading || this.options.title || ''),\n onclosed : function () {\n self.window=null;\n self.options.container=null;\n self.id=null;\n }\n })\n Code.later(1,() => {\n self.options.container=$('#'+self.id);\n this.tableID=Table(content,self.options);\n })\n }\n } else {\n if (this.tableID==undefined) {\n }\n if (this.tableID) this.options.replace=this.tableID;\n this.tableID=Table(content,this.options);\n } \n \n }\n input (data) {\n this.x=data;\n switch (this.action) {\n case 'info':\n this.info(data);\n break\n case 'log':\n this.log(data);\n break\n case 'plot':\n this.plot(data);\n break\n case 'table':\n this.table(data);\n break\n }\n }\n output (node) {\n // produces no output\n }\n}\n\n\nclass Filter {\n constructor(action,options) {\n options=options||{}\n this.options=options;\n this.next=[];\n this.action=action||'canny';\n this.window=null;\n this.ConsoleId=ConsoleId;\n this.ConsoleOutput=ConsoleOutput;\n switch (this.action) {\n case 'fft': \n var n=this.options.size;\n this._fft=this.options.dimension==2?Math.FFT.FFT(n,n):Math.FFT.FFT(n);\n break;\n }\n }\n get configuration () {\n return {\n canny : Select({\n data : Optional('MatrixTA'),\n _data : ['MatrixTA'],\n blur : 4,\n lowThr : 50,\n highThr : 100,\n filter : Optional(\"(data) => { return data }\"),\n }),\n sobel : Select({\n data : Optional('MatrixTA'),\n _data : ['MatrixTA'],\n blur : 4,\n filter : Optional(\"(data) => { return data }\"),\n }),\n $template : function (obj,options,subclass) {\n return '// source.output('+obj+')\\n// '+obj+'.output(target)';\n }\n }\n }\n input (data,internal) {\n var self=this;\n if (!internal && this.options.filter) data=this.options.filter.call(this,data);\n this.x=data;\n switch (this.action) {\n case 'canny':\n this.y=this.canny(data);\n break\n case 'sobel':\n this.y=this.sobel(data);\n break\n }\n this.next.forEach(function (node) {\n node.input(self.y);\n })\n return this.y;\n }\n output (node) {\n this.next.push(node);\n }\n set (key,val) {\n this.options[key]=val;\n if (this.x!=undefined) this.input(this.x,true);\n }\n canny (data) {\n // TODO: only Matrix Uint8 supported for now\n // Code.print('canny '+this.options.blur+', ['+this.options.lowThr+','+this.options.highThr+']');\n var jsfeat = ImageUtils.jsfeat;\n var n=data.rows,m=data.columns,blur=this.options.blur,\n lowthr=this.options.lowThr,highthr=this.options.highThr,\n buffer = data.data,\n img_u8 = new jsfeat.matrix_t(m, n, jsfeat.U8C1_t, new jsfeat.data_t(buffer.length,buffer));\n jsfeat.imgproc.gaussian_blur(img_u8, img_u8, blur);\n jsfeat.imgproc.canny(img_u8, img_u8, lowthr, highthr);\n var filtered = Math.MatrixTA({rows:n,columns:m,data:img_u8.data,layout:21});\n return filtered;\n }\n sobel (data) {\n var jsfeat = ImageUtils.jsfeat;\n // Code.print('sobel '+this.options.blur);\n var n=data.rows,m=data.columns,blur=this.options.blur,\n buffer = data.data,\n filtered = Math.MatrixTA.clone(data),\n img_u8 = new jsfeat.matrix_t(n, m, jsfeat.U8C1_t, new jsfeat.data_t(buffer.length,buffer));\n\n var img_gxgy = new jsfeat.matrix_t(m, n, jsfeat.S32C2_t);\n var img_mag = new jsfeat.matrix_t(m, n, jsfeat.S32C1_t);\n jsfeat.imgproc.gaussian_blur(img_u8, img_u8, blur);\n jsfeat.imgproc.sobel_derivatives(img_u8, img_gxgy);\n\n var i = img_u8.cols*img_u8.rows, gx = 0, gy = 0;\n var x=0,y=0,dx=0,dy=0;\n var agx=0, agy=0;\n var gd=img_gxgy.data, mag=img_mag.data, id=filtered.data;\n while(--i >= 0) {\n gx = gd[i<<1];\n gy = gd[(i<<1)+1];\n mag[i] = gx*gx + gy*gy;\n }\n // Code.print(img_u8.rows,img_u8.cols);\n for(y = 1; y < img_u8.rows - 1; ++y) {\n i = (y * img_u8.cols + 1)|0;\n for(x = 1 ; x < img_u8.cols - 1; ++x, ++i) {\n gx = gd[i<<1];\n gy = gd[(i<<1)+1];\n agx = ((gx ^ (gx >> 31)) - (gx >> 31))|0;\n agy = ((gy ^ (gy >> 31)) - (gy >> 31))|0;\n \n if(gx > 0) dx = 1;\n else dx = -1;\n \n if(gy > 0) dy = img_u8.cols;\n else dy = -img_u8.cols;\n \n var a1, a2, b1, b2, A, B, point;\n if(agx > agy) {\n a1 = mag[i+dx];\n a2 = mag[i+dx+(-dy)];\n b1 = mag[i-dx];\n b2 = mag[i-dx+dy];\n A = (agx - agy)*a1 + agy*a2;\n B = (agx - agy)*b1 + agy*b2;\n point = mag[i] * agx;\n if(point >= A && point > B) {\n id[i] = agx&0xff;\n }\n else {\n id[i] = 0x0;\n }\n } else {\n a1 = mag[i+(-dy)];\n a2 = mag[i+dx+(-dy)];\n b1 = mag[i+dy];\n b2 = mag[i-dx+dy];\n A = (agy - agx)*a1 + agx*a2;\n B = (agy - agx)*b1 + agx*b2;\n point = mag[i] * agy;\n if(point >= A && point > B) {\n id[i] = agy&0xff;\n }\n else {\n id[i] = 0x0;\n }\n }\n }\n }\n \n return filtered;\n }\n}\n\nexport { Data, Display, Filter }"},{"options":{"collapsed":false,"run":true},"text":"# Cellular Automata for Images\n\n## CA Modell\n\nThe cellular automaton (CA) consists of a computational logic and a fast visualizer. The visualizer uses a static discrete color palette.\n\nThe CA is defined by a model, generally consisting of world and cell definitions:\n\n```js\nvar model = {\n rows: rows,\n columns : number,\n neighbors : number,\n radius : number,\n palette : string [],\n cell : {\n size: number, // visual patch size\n state: { $:number },\n before? : function (x,y),\n activity : function (x,y),\n after?: function (x,y),\n init?: function (x,y),\n color: function (x,y) -> number,\n },\n parameter? : { $:* },\n data?: *, // typical a matrix\n input?: *,\n output?: *,\n}\n```\n\nThe *color* function returns an index of the color *palette* depending on the internal state of a cell. The neighborhoud is either Moore (8) or von-Neumann (4) with radius 1 or 2.\n\n\n### Cells\n\nA cell is defined by at least the *activity* function, optionally by its *init*, *before*, and *after* functions. Each CA update calls all of the *before*, *activity*, and *after* functions in a given (*scheduler*) seqeuential order. \n\nThe *activity* functions changes the state of the cell. The state of the cell consists of a set of variables (defined in *state*). There is typical one main state variable (related to the computational result) and auxiliary variables. The state transition bases on state values from the neighbourhood of each cell.\n\nThe *init* function can be used to initialize the state of each cell using external data, e.g., a Matrix with numbers (input image), provided by the *data* or *input* entries (global data).\n\nEach cell avriable and all global objects and functions can be accessed by the *this* object inside the cell functions.\n\n>! Don't use arrow functions!!!\n\nExample:\n\n```js\n{\n ..\n state : { z:0, z0:0 },\n before : function (x,y) { \n this.z0=this.z;\n },\n activity : function (x,y) {\n this.z = this.z0+\n this.ask(this.neighbors, 'count', 'z0', 1, '>');\n }\n color : function (x,y) { return this.z>0?1:0 },\n}\n```\n\nThe *ask* function is a expressive operation to explore the state of the pre-defined neighbourhood of each cell (`this.neighbors`). \n\n\n\n## CAI Model\n\nIn addition to the visual graphics, a real (RGB/Grayscale) image can be overlayed, commonly provdied by a TypedArray MatrixTA object:\n\n```js\nvar model ={\n ..\n image: MatrixTA,\n opacity?: number,\n opacity2d?: number,\n\n}\n```\n\n## Image Sets\n\nDifferent image filtering methods (edge detection) and object detection should be applied to a set of prepared (cropped segmented) micrograph images. The micrographs show the porosity of metal test samples produced by an additive manufacturing process usign metal powder and laser melting.\n\n"},{"options":{"eval":"_PxEnUf_ZnVuY3Rpb24gKHRleHQsZW52KSB7CiAgICAgICAgICAgICBqc1Njb3BlLnJ1bih0ZXh0LGVudikKICAgICAgICAgIH0=","mode":"javascript","label":"Init","heightC":100,"heightE":10,"collapsed":false,"overlay":2,"evalUser":"undefined"},"code":"description { Requires cai, math, and image plugins }\nload('cai.plugin')\nload('math.plugin')\nload('image.plugin')\nprint('Done.')"},{"options":{"eval":"_PxEnUf_ZnVuY3Rpb24gKHRleHQsZW52KSB7CiAgICAgICAgICAgICBqc1Njb3BlLnJ1bih0ZXh0LGVudikKICAgICAgICAgIH0=","mode":"javascript","label":"Synthetic Example CA Caves","heightC":100,"heightE":20,"collapsed":false,"overlay":2,"evalUser":"undefined"},"code":"parameter {A:4,B:6}\nvar rows=100,\n columns=100,\n size=4;\nvar image = Math.MatrixTA(rows,columns,{datatype:'Uint8'}).apply(() => Math.random.int(1,255));\nvar model = {\n rows: rows,\n columns : columns,\n neighbors : 8,\n radius : 1,\n palette : ['white','red'],\n datatype : 'Uint8',\n scheduler : 'UPRIGHT', // two phase all before; all process\n image:image,\n opacity:1,\n opacity2d: 0.5,\n cell : {\n name : 'wall',\n size : size,\n state : { open:0, wasOpen:0 },\n before : function (x,y) {\n this.wasOpen = this.open;\n },\n activity : function (x,y) {\n // neighbors: ordering [NW,N,NW,W,E,SW,S,SE]\n var surrounding = this.ask(this.neighbors, 'count', 'wasOpen', 1);\n this.open = (this.wasOpen && surrounding >= this.A) || surrounding >= this.B?1:0;\n },\n init : function (x,y) {\n this.open = this.data.get(y,x)>100?1:0;\n },\n color : function (x,y) { return this.open ? 0:1 },\n },\n parameter : {\n A:A,\n B:B\n },\n data : image\n}\n// Important: wrapper id for cleanup!!!\nvar container = $('',{id:'wrapper'+Utils.uniqueID(8)});\nprint(container);\nvar ca1 = new CAI.CA(model,{\n container:container\n});\nca1.init();\nexport { ca1 }"},{"options":{"eval":"_PxEnUf_ZnVuY3Rpb24gKHRleHQsZW52KSB7CiAgICAgICAgICAgICBqc1Njb3BlLnJ1bih0ZXh0LGVudikKICAgICAgICAgIH0=","mode":"javascript","label":"Step CA","heightC":100,"heightE":10,"collapsed":false,"overlay":2,"evalUser":"undefined"},"code":"import { ca1 }\nca1.step(1)"},{"options":{"collapsed":false,"run":true},"text":">? Vary parameters A and B. Which effect on the global emergence can be observed?\n\n>< ???\n\n## Image Processing\n\n- Use the `ADMSchliff1Samples.sql` data base containing micrographs of additively manufactured metal probes\n + The micrographs show pores and impurities\n \n- Place the data base file in the sqld folder ans start sqld:\n\n```\nsqld root:9999\n```"},{"options":{"eval":"_PxEnUf_ZnVuY3Rpb24gKHRleHQsZW52KSB7CiAgICAgICAgICAgICBqc1Njb3BlLnJ1bih0ZXh0LGVudikKICAgICAgICAgIH0=","mode":"javascript","label":"Load Image Data","heightC":100,"heightE":10,"collapsed":false,"overlay":2,"evalUser":"undefined"},"code":"parameter { sqlURL:\"localhost:9999\", database:\"ADMSchliff1Samples\", table:\"images1S\" }\nimport {Data}\ndescription { Loads selected cropped image segments from ADMSchliff1Samples.images1S table }\nvar images=[]\nvar data = new Data(\n \"sql\",\n {\n \"url\": sqlURL,\n \"database\": database,\n \"table\": table\n }\n);\nprint(await data.start());\nprint(data._tables)\nvar rows = await data.rows(),\n schema = await data.type();\nprint('#rows='+rows);\nprint('schema',schema) \n//data.read(\"*\")\nfor(var i=0;i