This repository has been archived by the owner on Dec 4, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
wenzl
committed
Jan 11, 2018
1 parent
bc38c21
commit 7427966
Showing
22 changed files
with
941 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Compiled Object files, Static and Dynamic libs (Shared Objects) | ||
*.o | ||
*.a | ||
*.so | ||
|
||
# Folders | ||
_obj | ||
_test | ||
|
||
# Architecture specific extensions/prefixes | ||
*.[568vq] | ||
[568vq].out | ||
|
||
*.cgo1.go | ||
*.cgo2.c | ||
_cgo_defun.c | ||
_cgo_gotypes.go | ||
_cgo_export.* | ||
|
||
_testmain.go | ||
|
||
*.exe | ||
*.test | ||
*.prof | ||
.DS_Store | ||
.vscode/ | ||
vendor/*/ | ||
images | ||
cmd/images | ||
cmd/config.yml | ||
cmd/cmd | ||
cmd/bin | ||
qanswer | ||
config.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
build: | ||
cd cmd && go build -o ../qanswer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# 快速答题 | ||
|
||
《冲顶大会》,《百万英雄》... 答题神奇,顺利吃鸡! | ||
|
||
通过抓取手机屏幕截图经过文字识别,结合搜索引擎给出一个参考值。 | ||
|
||
 | ||
|
||
分析结果: | ||
|
||
 | ||
|
||
**结果说明:** | ||
|
||
- 结果数:通过题目+答案的搜索形式在搜索引擎中的结果数量 | ||
|
||
- 答案出现频率:通过搜索题目,答案在第一页结果中出现的频率 | ||
|
||
结果并不是100%的,只给出一个参考值,还需用户自己判断。理论上可支持多款APP,只需要修改`config.yml`中的题目和答案的截取位置即可。 | ||
|
||
|
||
## 编译安装 | ||
|
||
安装go环境,执行 `make build` 生成`qanswer`可执行文件。 | ||
|
||
### 配置文件参考 | ||
执行`qanswer`时,默认读取当前目录下的`config.yml`配置文件。 | ||
|
||
``` | ||
# 是否开始调试模式 | ||
debug: false | ||
# 对应的设备类型:ios or android | ||
device: ios | ||
# 使用的ocr工具:baidu or tesseract | ||
ocr_type: baidu | ||
# ios 设备连接wda的地址 | ||
wda_address: '127.0.0.1:8100' | ||
# 截取题目的位置 : | ||
question_x: 30 | ||
question_y: 310 | ||
question_w: 650 | ||
question_h: 135 | ||
# 截取答案的位置 | ||
answer_x: 30 | ||
answer_y: 500 | ||
answer_w: 680 | ||
answer_h: 370 | ||
#当选用baidu ocr时,需要执行api_key和secret_key | ||
baidu_api_key: "xxx...." | ||
baidu_secret_key: "xxx...." | ||
``` | ||
|
||
### iOS | ||
|
||
- 安装WDA :[iOS 真机如何安装 WebDriverAgent](https://testerhome.com/topics/7220) | ||
- 编译得到`qanswer`文件 | ||
- 根据设备尺寸以及答题APP,修改题目和答案截取位置参数 | ||
- 开始 | ||
|
||
|
||
### Android | ||
|
||
> TIPS:未验证 | ||
|
||
|
||
## 百度ocr | ||
`ocr_type: baidu` | ||
|
||
如果使用[百度ocr](https://cloud.baidu.com/product/ocr.html),则需要预先申请api key 和secret key ,并且免费的额度有限 | ||
|
||
## tesseract | ||
`ocr_type: tesseract` | ||
|
||
安装tesseract以及简体中文包。 | ||
|
||
以mac:为例 | ||
|
||
``` | ||
brew install tesseract | ||
cd /usr/local/Cellar/tesseract/{version}/share/tessdata | ||
wget https://github.com/tesseract-ocr/tessdata/raw/master/chi_sim.traineddata | ||
``` | ||
|
||
其他系统的安装说明:https://github.com/tesseract-ocr/tesseract/wiki | ||
|
||
|
||
### TODO: | ||
|
||
- 验证android adb方式 | ||
- 更友好,更快的展示 | ||
- 支持google搜索 | ||
- 不同机型,不同答题app的配置参数 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
output: ./bin/qanswer | ||
watch_paths: | ||
- ../ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package main | ||
|
||
import "github.com/silenceper/qanswer" | ||
|
||
func main() { | ||
qanswer.Run() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package config | ||
|
||
import ( | ||
"io/ioutil" | ||
"path/filepath" | ||
|
||
yaml "gopkg.in/yaml.v1" | ||
) | ||
|
||
//Config 全局配置 | ||
type Config struct { | ||
Debug bool `yaml:"debug"` | ||
Device string `yaml:"device"` | ||
OcrType string `yaml:"ocr_type"` | ||
WdaAddress string `yaml:"wda_address"` | ||
|
||
//baidu ocr | ||
BaiduAPIKey string `yaml:"baidu_api_key"` | ||
BaiduSecretKey string `yaml:"baidu_secret_key"` | ||
|
||
//截图题目位置 | ||
QuestionX int `yaml:"question_x"` | ||
QuestionY int `yaml:"question_y"` | ||
QuestionW int `yaml:"question_w"` | ||
QuestionH int `yaml:"question_h"` | ||
|
||
//截取答案位置 | ||
AnswerX int `yaml:"answer_x"` | ||
AnswerY int `yaml:"answer_y"` | ||
AnswerW int `yaml:"answer_w"` | ||
AnswerH int `yaml:"answer_h"` | ||
} | ||
|
||
var cfg *Config | ||
|
||
//GetConfig 解析配置 | ||
func GetConfig() *Config { | ||
if cfg != nil { | ||
return cfg | ||
} | ||
filename, _ := filepath.Abs("./config.yml") | ||
yamlFile, err := ioutil.ReadFile(filename) | ||
|
||
if err != nil { | ||
panic(err) | ||
} | ||
var c *Config | ||
err = yaml.Unmarshal(yamlFile, &c) | ||
if err != nil { | ||
panic(err) | ||
} | ||
cfg = c | ||
return cfg | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
output: ./bin/qanswer | ||
watch_paths: | ||
- ../ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package qanswer | ||
|
||
import ( | ||
"fmt" | ||
"image" | ||
|
||
"github.com/ngaut/log" | ||
"github.com/silenceper/qanswer/config" | ||
"github.com/silenceper/qanswer/proto" | ||
"github.com/silenceper/qanswer/util" | ||
) | ||
|
||
func saveImage(png image.Image, cfg *config.Config) error { | ||
screenshotPath := fmt.Sprintf("%s/screenshot.png", proto.ImagePath) | ||
err := util.SavePNG(screenshotPath, png) | ||
if err != nil { | ||
return fmt.Errorf("保存截图失败,%v", err) | ||
} | ||
log.Debugf("保存完整截图成功,%s", screenshotPath) | ||
|
||
//裁剪图片 | ||
questionImg, answerImg, err := cutImage(png, cfg) | ||
if err != nil { | ||
return fmt.Errorf("截图失败,%v", err) | ||
} | ||
err = util.SavePNG(proto.QuestionImage, questionImg) | ||
if err != nil { | ||
return fmt.Errorf("保存question截图失败,%v", err) | ||
} | ||
log.Debugf("保存question截图成功") | ||
err = util.SavePNG(proto.ImagePath+"/answer.png", answerImg) | ||
if err != nil { | ||
return fmt.Errorf("保存answer截图失败,%v", err) | ||
|
||
} | ||
log.Debugf("保存answer截图成功") | ||
return nil | ||
} | ||
|
||
func cutImage(src image.Image, cfg *config.Config) (questionImg image.Image, answerImg image.Image, err error) { | ||
questionImg, err = util.CutImage(src, cfg.QuestionX, cfg.QuestionY, cfg.QuestionW, cfg.QuestionH) | ||
//questionImg, err = util.CutImage(src, 30, 310, 650, 135) | ||
if err != nil { | ||
return | ||
} | ||
|
||
answerImg, err = util.CutImage(src, cfg.AnswerX, cfg.AnswerY, cfg.AnswerW, cfg.AnswerH) | ||
// answerImg, err = util.CutImage(src, 30, 500, 680, 370) | ||
if err != nil { | ||
return | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package qanswer | ||
|
||
import ( | ||
"github.com/silenceper/qanswer/config" | ||
"github.com/silenceper/qanswer/ocr" | ||
"github.com/silenceper/qanswer/proto" | ||
) | ||
|
||
//Ocr ocr 识别图片文字 | ||
type Ocr interface { | ||
GetText(imgPath string) (string, error) | ||
} | ||
|
||
//NewOcr 使用哪种ocr识别 | ||
func NewOcr(cfg *config.Config) Ocr { | ||
if cfg.OcrType == proto.OcrTesseract { | ||
return ocr.NewTesseract(cfg) | ||
} | ||
return ocr.NewBaidu(cfg) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package ocr | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/url" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/silenceper/qanswer/config" | ||
"github.com/silenceper/qanswer/util" | ||
) | ||
|
||
//Baidu baidu ocr api | ||
type Baidu struct { | ||
apiKey string | ||
secretKey string | ||
|
||
accessToken string | ||
sync.RWMutex | ||
} | ||
|
||
type accessTokenRes struct { | ||
AccessToken string `json:"access_token"` | ||
ExpiresIn int32 `json:"expires_in"` | ||
} | ||
|
||
//wordsResults 匹配 | ||
type wordsResults struct { | ||
WordsNum int32 `json:"words_result_num"` | ||
WordsResult []struct { | ||
Words string `json:"words"` | ||
} `json:"words_result"` | ||
} | ||
|
||
//NewBaidu new | ||
func NewBaidu(cfg *config.Config) *Baidu { | ||
baidu := new(Baidu) | ||
baidu.apiKey = cfg.BaiduAPIKey | ||
baidu.secretKey = cfg.BaiduSecretKey | ||
return baidu | ||
} | ||
|
||
//GetText 识别图片中的文字 | ||
func (baidu *Baidu) GetText(imgPath string) (string, error) { | ||
accessToken, err := baidu.getAccessToken() | ||
if err != nil { | ||
return "", err | ||
} | ||
base64Data, err := util.OpenImageToBase64(imgPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
uri := fmt.Sprintf("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=%s", accessToken) | ||
|
||
postData := url.Values{} | ||
postData.Add("image", base64Data) | ||
body, err := util.PostForm(uri, postData) | ||
if err != nil { | ||
return "", err | ||
} | ||
wordResults := new(wordsResults) | ||
err = json.Unmarshal(body, wordResults) | ||
if err != nil { | ||
return "", err | ||
} | ||
var text string | ||
for _, words := range wordResults.WordsResult { | ||
text = fmt.Sprintf("%s\n%s", text, strings.TrimSpace(words.Words)) | ||
} | ||
text = strings.TrimLeft(text, "\n") | ||
return text, nil | ||
} | ||
|
||
func (baidu *Baidu) getAccessToken() (accessToken string, err error) { | ||
baidu.Lock() | ||
defer baidu.Unlock() | ||
if baidu.accessToken != "" { | ||
accessToken = baidu.accessToken | ||
return | ||
} | ||
uri := fmt.Sprintf("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s", baidu.apiKey, baidu.secretKey) | ||
body, e := util.PostJSON(uri, nil, "application/json;charset=utf-8") | ||
if e != nil { | ||
err = e | ||
return | ||
} | ||
res := new(accessTokenRes) | ||
err = json.Unmarshal(body, res) | ||
if err != nil { | ||
return | ||
} | ||
accessToken = res.AccessToken | ||
baidu.accessToken = accessToken | ||
return | ||
} |
Oops, something went wrong.