ocr.js 7.2 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. debugger;
  138. // 6.提取英文单词
  139. const engTexts=this.extractEnglishWords(texts);
  140. // let arr=[];
  141. // for(var i=0;i<engTexts.length;i++){
  142. // let obj={};
  143. // obj.Word=engTexts[i];
  144. // obj.Selected=0;
  145. // arr.push(obj);
  146. // }
  147. app.globalData.OCRWords=engTexts;
  148. //debugger;
  149. wx.redirectTo({
  150. url: "./selectword",
  151. });
  152. that.setData({
  153. cameraActive: true,
  154. })
  155. } catch (err) {
  156. console.error('OCR处理失败:', err)
  157. wx.showToast({
  158. title: err.message || '识别失败',
  159. icon: 'none',
  160. duration: 3000
  161. })
  162. } finally {
  163. wx.hideLoading();
  164. that.setData({
  165. cameraActive: true,
  166. })
  167. }
  168. },
  169. // 加强坐标转换
  170. convertPosition(polygon) {
  171. try {
  172. const points = polygon.Points || []
  173. if (points.length === 0) {
  174. return { x: 50, y: 50, width: 200, height: 30 }
  175. }
  176. const xs = points.map(p => p.X || 0)
  177. const ys = points.map(p => p.Y || 0)
  178. return {
  179. x: Math.min(...xs),
  180. y: Math.min(...ys),
  181. width: Math.max(...xs) - Math.min(...xs),
  182. height: Math.max(...ys) - Math.min(...ys)
  183. }
  184. } catch (e) {
  185. return { x: 50, y: 50, width: 200, height: 30 }
  186. }
  187. },
  188. // 提取英语单词的函数 - 增强版
  189. extractEnglishWords(texts) {
  190. //console.group('英语单词提取');
  191. const words = new Set();
  192. texts.forEach(item => {
  193. const text = item.text;
  194. //console.log('处理文本:', text);
  195. // 使用多种分隔符分割文本(空格、逗号、句号、感叹号、中文字符等)
  196. // 这个正则表达式会匹配任何非英文字母、撇号或连字符的字符作为分隔符
  197. const parts = text.split(/[^A-Za-z''-]+/).filter(Boolean);
  198. //console.log('分割后的部分:', parts);
  199. // 处理每个可能的单词
  200. parts.forEach(part => {
  201. // 清理并验证单词
  202. const cleanWord = this.cleanWord(part);
  203. // 特殊处理单词"I"
  204. if (cleanWord === 'I' || cleanWord === 'a') {
  205. words.add(cleanWord); // 添加小写的"i"
  206. //console.log('添加单词: I (特殊处理)');
  207. }
  208. // 处理其他单词(长度>=2)
  209. else if (cleanWord && cleanWord.length >= 2 && /^[A-Za-z''-]+$/.test(cleanWord)) {
  210. let lowerWord = cleanWord.toLowerCase();
  211. if (lowerWord=="i'm"){
  212. lowerWord="I'm";
  213. }
  214. words.add(lowerWord);
  215. //console.log('添加单词:', lowerWord);
  216. }
  217. });
  218. });
  219. //const result = Array.from(words).sort();
  220. let result = Array.from(words);
  221. result = common.removeDuplicateAndTrimStrings(result);
  222. //console.log('提取结果:', result);
  223. //console.groupEnd();
  224. return result;
  225. },
  226. // 清理单词,去除非字母字符
  227. cleanWord(word) {
  228. if (!word) return '';
  229. // 去除单词前后的非字母字符
  230. const cleaned = word.replace(/^[^A-Za-z]+|[^A-Za-z]+$/g, '');
  231. // 保留单词中间的撇号和连字符
  232. return cleaned.replace(/[^A-Za-z''-]/g, '');
  233. },
  234. onShareAppMessage: function () {
  235. return {
  236. title: app.globalData.ShareTitle,
  237. path: app.globalData.SharePath + '?UserID=' + app.globalData.userInfo.UserID,
  238. imageUrl: app.globalData.ShareImage,
  239. }
  240. },
  241. })