import * as fs from 'fs'; | |
import * as cv from '../../'; | |
import { | |
lccs, | |
centerLetterInImage, | |
saveConfusionMatrix | |
} from './OCRTools'; | |
const trainDataPath = '../../data/ocr/traindata'; | |
const testDataPath = '../../data/ocr/testdata'; | |
const outPath = '../../data/ocr'; | |
const SVMFile = 'lcletters.xml'; | |
const hog = new cv.HOGDescriptor({ | |
winSize: new cv.Size(40, 40), | |
blockSize: new cv.Size(20, 20), | |
blockStride: new cv.Size(10, 10), | |
cellSize: new cv.Size(10, 10), | |
L2HysThreshold: 0.2, | |
nbins: 9, | |
gammaCorrection: true, | |
signedGradient: true | |
}); | |
const svm = new cv.SVM({ | |
kernelType: cv.ml.SVM.RBF, | |
c: 12.5, | |
gamma: 0.50625 | |
}); | |
const computeHOGDescriptorFromImage = (img: cv.Mat, isIorJ: boolean) => { | |
let im = img; | |
if (im.rows !== 40 || im.cols !== 40) { | |
im = im.resize(40, 40); | |
} | |
// center the letter | |
im = centerLetterInImage(img, isIorJ); | |
if (!img) { | |
return null; | |
} | |
return hog.compute(im); | |
}; | |
const trainSVM = (trainDataFiles: string[][], isAuto: boolean = false) => { | |
// make hog features of trainingData and label it | |
console.log('make features'); | |
const samples: number[][] = []; | |
const labels: number[] = []; | |
trainDataFiles.forEach((files, label) => { | |
files.forEach((file) => { | |
const img = cv.imread(file); | |
const isIorJ = label === 8 || label === 9; | |
const desc = computeHOGDescriptorFromImage(img, isIorJ); | |
if (!desc) { | |
return; | |
} | |
samples.push(desc); | |
labels.push(label); | |
}); | |
}); | |
// train the SVM | |
console.log('training'); | |
const trainData = new cv.TrainData( | |
new cv.Mat(samples, cv.CV_32F), | |
cv.ml.ROW_SAMPLE, | |
new cv.Mat([labels], cv.CV_32S) | |
); | |
svm[isAuto ? 'trainAuto' : 'train'](trainData); | |
}; | |
const data = lccs.map((letter) => { | |
const trainDataDir = `${trainDataPath}/${letter}`; | |
const testDataDir = `${testDataPath}/${letter}`; | |
const train = fs.readdirSync(trainDataDir).map(file => `${trainDataDir}/${file}`); | |
const test = fs.readdirSync(testDataDir).map(file => `${testDataDir}/${file}`); | |
return ({ train, test }); | |
}); | |
const trainDataFiles = data.map(classData => classData.train); | |
const testDataFiles = data.map(classData => classData.test); | |
const numTrainImagesPerClass = trainDataFiles[0].length; | |
const numTestImagesPerClass = testDataFiles[0].length; | |
console.log('train data per class:', numTrainImagesPerClass); | |
console.log('test data per class:', numTestImagesPerClass); | |
trainSVM(trainDataFiles, false); | |
svm.save(`${outPath}/${SVMFile}`); | |
svm.load(`${outPath}/${SVMFile}`); | |
// compute prediction error for each letter | |
const errs = Array(26).fill(0); | |
testDataFiles.forEach((files, label) => { | |
files.forEach((file) => { | |
const img = cv.imread(file); | |
const isIorJ = label === 8 || label === 9; | |
const desc = computeHOGDescriptorFromImage(img, isIorJ); | |
if (!desc) { | |
throw new Error(`Computing HOG descriptor failed for file: ${file}`); | |
} | |
const predictedLabel = svm.predict(desc); | |
if (label !== predictedLabel) { | |
errs[label] += 1; | |
} | |
}); | |
}); | |
console.log('prediction result:'); | |
errs.forEach((err, l) => console.log(lccs[l], err, 1 - (err / numTestImagesPerClass))); | |
console.log('average: ', 1 - (errs.reduce((e1, e2) => e1 + e2) / (lccs.length * numTestImagesPerClass))); | |
saveConfusionMatrix( | |
testDataFiles, | |
(img, isIorJ) => svm.predict(computeHOGDescriptorFromImage(img, isIorJ)), | |
numTestImagesPerClass, | |
`${outPath}/confusionmatrix.csv` | |
); |
完形心理學!?讓我們了解“介面設計師”為什麼這樣設計 — 說服客戶與老闆、跟工程師溝通、強化設計概念的有感心理學 — 情況 1 : 為何要留那麼多空白? 害我還要滾動滑鼠(掀桌) 情況 2 : 為什麼不能直接用一頁展現? 把客戶的需求塞滿不就完工啦! (無言) 情況 3: 這種設計好像不錯,但是為什麼要這樣做? (直覺大神告訴我這樣設計,但我說不出來為什麼..) 雖然世界上有許多 GUI 已經走得又長又遠又厲害,但別以為這種古代人對話不會出現,一直以來我們只是習慣這些 GUI 被如此呈現,但為何要這樣設計我們卻不一定知道。 由於 完形心理學 歸納出人類大腦認知之普遍性的規則,因此無論是不是 UI/UX 設計師都很適合閱讀本篇文章。但還是想特別強調,若任職於傳統科技公司,需要對上說服老闆,需要平行說服(資深)工程師,那請把它收進最愛;而習慣套用設計好的 UI 套件,但不知道為何這樣設計的 IT 工程師,也可以透過本文來強化自己的產品說服力。 那就開始吧~(擊掌) 完形心理學,又稱作格式塔(Gestalt)心理學,於二十世紀初由德國心理學家提出 — 用以說明人類大腦如何解釋肉眼所觀察到的事物,並轉化為我們所認知的物件。它可說是現代認知心理學的基礎,其貫徹的概念就是「整體大於個體的總合 “The whole is other than the sum of the parts.” — Kurt Koffka」。 若深究完整的理論將會使本文變得非常的艱澀,因此筆者直接抽取個人認為與 UI 設計較為相關的 7 個原則(如下),並搭配實際案例做說明。有興趣了解全部理論的話可以另外 Google。 1. 相似性 (Similarity) — 我們的大腦會把相似的事物看成一體 如果數個元素具有類似的尺寸、體積、顏色,使用者會自動為它們建立起關聯。這是因為我們的眼睛和大腦較容易將相似的事物組織在一起。如下圖所示,當一連串方塊和一連串的圓形並排時,我們會看成(a)一列方塊和兩列圓形(b)一排圓形和兩排三角形。 對應用到介面設計上,FB 每則文章下方的按鈕圖標(按讚 Like / 留言Comment / 分享 Share)雖然功能各不相同,但由於它們在視覺上顏色、大小、排列上的相似性,用戶會將它們視認為...
留言
張貼留言