ocr.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import common from '../../utils/util';
  2. import main from '../../utils/main';
  3. const app = getApp();
  4. Page({
  5. data: {
  6. Menu:[],
  7. cameraActive: true,
  8. },
  9. onLoad: function (options) {
  10. app.globalData.OCRWords=[];
  11. main.checkGenerating();
  12. },
  13. onShow:function(){
  14. let that = this;
  15. that.setData({
  16. cameraActive: true,
  17. Menu:[{Name:"相册",CSS:"",Fun:"chooseImage"},{Name:"拍照",CSS:"Selected",Fun:"retake"}]
  18. });
  19. },
  20. retake() {
  21. this.data.Menu[0].CSS="";
  22. this.data.Menu[1].CSS="Selected";
  23. this.setData({
  24. Menu:this.data.Menu,
  25. cameraActive: true,
  26. });
  27. },
  28. // 拍照识别
  29. takePhoto() {
  30. const ctx = wx.createCameraContext();
  31. ctx.takePhoto({
  32. quality: 'high',
  33. success: (res) => {
  34. this.setData({
  35. imageUrl: res.tempImagePath,
  36. cameraActive: false,
  37. })
  38. this.performOCR(res.tempImagePath)
  39. },
  40. fail: (err) => {
  41. console.error('拍照失败:', err)
  42. wx.showToast({
  43. title: '拍照失败,请重试',
  44. icon: 'none'
  45. })
  46. }
  47. })
  48. },
  49. chooseImage() {
  50. this.data.Menu[0].CSS="Selected";
  51. this.data.Menu[1].CSS="";
  52. this.setData({
  53. Menu:this.data.Menu,
  54. cameraActive: false,
  55. });
  56. wx.chooseMedia({
  57. count: 1,
  58. mediaType: ['image'],
  59. sourceType: ['album'],
  60. success: (res) => {
  61. const tempFilePath = res.tempFiles[0].tempFilePath
  62. this.setData({
  63. imageUrl: tempFilePath,
  64. cameraActive: false,
  65. })
  66. this.performOCR(tempFilePath)
  67. },
  68. fail: (err) => {
  69. console.error('选择图片失败:', err)
  70. wx.showToast({
  71. title: '选择图片失败',
  72. icon: 'none'
  73. })
  74. }
  75. })
  76. },
  77. // 加强版的OCR识别方法
  78. async performOCR(imagePath) {
  79. let that=this;
  80. if (!imagePath) {
  81. console.error('图片路径无效')
  82. }
  83. wx.showLoading({ title: '识别中...', mask: true })
  84. try {
  85. // 1. 压缩图片
  86. const compressed = await new Promise((resolve, reject) => {
  87. wx.compressImage({
  88. src: imagePath,
  89. quality: 70,
  90. success: resolve,
  91. fail: () => resolve({ tempFilePath: imagePath })
  92. })
  93. })
  94. //console.log("1");
  95. // 2. 转换为base64
  96. const fileRes = await new Promise((resolve, reject) => {
  97. wx.getFileSystemManager().readFile({
  98. filePath: compressed.tempFilePath,
  99. encoding: 'base64',
  100. success: resolve,
  101. fail: reject
  102. })
  103. })
  104. //console.log("2");
  105. // 3. 调用云函数(添加超时处理)
  106. let postData={ ImageBase64: `data:image/jpeg;base64,${fileRes.data}` };
  107. let url = common.Encrypt("OCRImageData?UserID="+app.globalData.userInfo.UserID);
  108. url=app.globalData.serverUrl+url;
  109. //console.log("url:"+url);
  110. const cloudRes = await new Promise((resolve, reject) => {
  111. wx.request({
  112. url: url,
  113. method: "POST",
  114. data: postData,
  115. success: resolve,
  116. fail: reject,
  117. })
  118. });
  119. //console.log("3");
  120. // 4. 验证返回结果
  121. if (!cloudRes || !cloudRes.data.result) {
  122. throw new Error('无效的响应格式')
  123. }
  124. //console.log("4");
  125. if (!cloudRes.data.result) {
  126. throw new Error(cloudRes.data.result.message || '识别服务返回空数据')
  127. }
  128. // 5. 处理识别结果
  129. const texts = cloudRes.data.result.TextDetections.map(item => ({
  130. text: item.DetectedText || '未识别到文字',
  131. pos: this.convertPosition(item.ItemPolygon || { Points: [] })
  132. })).filter(item => item.DetectedText !== '未识别到文字')
  133. //console.log("5");
  134. if (texts.length === 0) {
  135. throw new Error('未识别到有效文字')
  136. }
  137. // 6.提取英文单词
  138. const engTexts=this.extractEnglishWords(texts);
  139. // let arr=[];
  140. // for(var i=0;i<engTexts.length;i++){
  141. // let obj={};
  142. // obj.Word=engTexts[i];
  143. // obj.Selected=0;
  144. // arr.push(obj);
  145. // }
  146. app.globalData.OCRWords=engTexts;
  147. //debugger;
  148. wx.redirectTo({
  149. url: "./selectword",
  150. });
  151. that.setData({
  152. cameraActive: true,
  153. })
  154. } catch (err) {
  155. console.error('OCR处理失败:', err)
  156. wx.showToast({
  157. title: err.message || '识别失败',
  158. icon: 'none',
  159. duration: 3000
  160. })
  161. } finally {
  162. wx.hideLoading();
  163. that.setData({
  164. cameraActive: true,
  165. })
  166. }
  167. },
  168. // 加强坐标转换
  169. convertPosition(polygon) {
  170. try {
  171. const points = polygon.Points || []
  172. if (points.length === 0) {
  173. return { x: 50, y: 50, width: 200, height: 30 }
  174. }
  175. const xs = points.map(p => p.X || 0)
  176. const ys = points.map(p => p.Y || 0)
  177. return {
  178. x: Math.min(...xs),
  179. y: Math.min(...ys),
  180. width: Math.max(...xs) - Math.min(...xs),
  181. height: Math.max(...ys) - Math.min(...ys)
  182. }
  183. } catch (e) {
  184. return { x: 50, y: 50, width: 200, height: 30 }
  185. }
  186. },
  187. // 提取英语单词的函数 - 增强版
  188. extractEnglishWords(texts) {
  189. //console.group('英语单词提取');
  190. const words = new Set();
  191. texts.forEach(item => {
  192. const text = item.text;
  193. //console.log('处理文本:', text);
  194. // 使用多种分隔符分割文本(空格、逗号、句号、感叹号、中文字符等)
  195. // 这个正则表达式会匹配任何非英文字母、撇号或连字符的字符作为分隔符
  196. const parts = text.split(/[^A-Za-z''-]+/).filter(Boolean);
  197. //console.log('分割后的部分:', parts);
  198. // 处理每个可能的单词
  199. parts.forEach(part => {
  200. // 清理并验证单词
  201. const cleanWord = this.cleanWord(part);
  202. // 特殊处理单词"I"
  203. if (cleanWord === 'I' || cleanWord === 'a') {
  204. words.add(cleanWord); // 添加小写的"i"
  205. //console.log('添加单词: I (特殊处理)');
  206. }
  207. // 处理其他单词(长度>=2)
  208. else if (cleanWord && cleanWord.length >= 2 && /^[A-Za-z''-]+$/.test(cleanWord)) {
  209. let lowerWord = cleanWord.toLowerCase();
  210. if (lowerWord=="i'm"){
  211. lowerWord="I'm";
  212. }
  213. words.add(lowerWord);
  214. //console.log('添加单词:', lowerWord);
  215. }
  216. });
  217. });
  218. //const result = Array.from(words).sort();
  219. let result = Array.from(words);
  220. result = common.removeDuplicateAndTrimStrings(result);
  221. //console.log('提取结果:', result);
  222. //console.groupEnd();
  223. return result;
  224. },
  225. // 清理单词,去除非字母字符
  226. cleanWord(word) {
  227. if (!word) return '';
  228. // 去除单词前后的非字母字符
  229. const cleaned = word.replace(/^[^A-Za-z]+|[^A-Za-z]+$/g, '');
  230. // 保留单词中间的撇号和连字符
  231. return cleaned.replace(/[^A-Za-z''-]/g, '');
  232. },
  233. onShareAppMessage: function () {
  234. return {
  235. title: app.globalData.ShareTitle,
  236. path: app.globalData.SharePath + '?UserID=' + app.globalData.userInfo.UserID,
  237. imageUrl: app.globalData.ShareImage,
  238. }
  239. },
  240. })