效率提升-其他工具

86 阅读2分钟

简介

  前面介绍了很多稍微大型点的工具,剩下还有一些工具我觉得也是对我帮助挺多的,下面简单介绍下。

正则表达式

  正则表达式来说,熟悉的人很熟悉,不熟悉人的简直太痛苦了,我日常工作中正则表达式算用的中规中矩吧,不算多也不算少,当然网上有很多校验正则表达式的工具。我做这个工具的目的,也是需要有个可以调试和记录正则表达式的地方。

帮助说明

表达式校验

JSON格式化

  这个工具用的也是比较多,经常调试网页的时候,需要对一个字符串进行格式化处理或者查看,就会把字符串复制到这里,然后格式化。功能很简单,但是很实用,当然互联网上这种也很多。

Vscode代码片段

  程序员开发或者写文档的过程中,代码片段是个很好用的东西,可以把你常用的一些片段封装起来,后续用到的时候输入指定关键字,可以把片段输出来,可以节省大量的时间。我代码片段用的非常多。

  代码片段是好用,但是vscode中配置复杂代码片段的时候挺麻烦的,它是一个数组,需要你一行一行的去组织,如果碰到页面级的代码片段,那就要花很多时间去处理。所以我就做了一个转换工具,把你需要的代码片段内容复制进去,自动输出vscode需要的代码片段格式,一键复制到vscode就行了。

OCR识别

  OCR识别的需求,也是源于很多人发错误信息什么的,会拍照或者截图过来,然后复制不出来,要自己手动一个个敲,太难受了,所以就想办法做了一个OCR识别的小功能。

本身没有OCR识别的能力,是借助了百度云的OCR功能,每个账户有免费的限额,我自己用用基本上足够了。

识别后的结果

主要逻辑就是通过百度云SDK识别图片,获取识别的文字内容和文字坐标,然后前端通过这些信息,把识别后的图绘制出来

后端

import com.baidu.aip.ocr.AipOcr;
//百度云官网有详细的ocr识别说明
public List<RecognizeResult> getOcrText(MultipartFile multipartFile) throws IOException, JSONException {

        String md5 = FileUtils.getMd5(multipartFile);
        OcrEntity ocrEntity = ocrDao.getOcr(md5);
        if(ocrEntity!=null){
            String content  = ocrEntity.getResult();
            CollectionType listType  = objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, RecognizeResult.class);
            return objectMapper.readValue(content, listType);
        }

        // 初始化一个AipOcr
        AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);

        // 可选:设置网络连接参数
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);

        System.setProperty("aip.log4j.conf", "log4j.properties");

        HashMap<String, String> options = new HashMap<>(10);
        options.put("language_type", "CHN_ENG");
        options.put("detect_direction", BOOLEAN_FALSE.toString());
        options.put("detect_language", BOOLEAN_FALSE.toString());
        options.put("vertexes_location", BOOLEAN_TRUE.toString());
        options.put("probability", BOOLEAN_FALSE.toString());
        JSONObject jsonObject = client.accurateGeneral(multipartFile.getBytes(), options);
        JSONArray jsonArray = jsonObject.getJSONArray("words_result");
        List<RecognizeResult> recognizeResultList = new LinkedList<>();
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject jsonObjectBase = jsonArray.getJSONObject(i);
            String words = jsonObjectBase.getString("words");
            RecognizeResult recognizeResult = new RecognizeResult();
            recognizeResult.setWords(words);
            JSONObject jsonLocation = jsonObjectBase.getJSONObject("location");
            RecognizeLocationResult recognizeLocationResult=new RecognizeLocationResult();
            //获取识别结果的坐标位置,便于前端绘制
            recognizeLocationResult.setHeight(jsonLocation.getInt("height"));
            recognizeLocationResult.setWidth(jsonLocation.getInt("width"));
            recognizeLocationResult.setTop(jsonLocation.getInt("top"));
            recognizeLocationResult.setLeft(jsonLocation.getInt("left"));
            recognizeResult.setLocation(recognizeLocationResult);
            recognizeResultList.add(recognizeResult);
        }

        //写入数据库,识别一次,下次就不识别了
        OcrEntity ocrEntityInsert = new OcrEntity();
        ocrEntityInsert.setId(UUID.randomUUID().toString());
        ocrEntityInsert.setMd5(md5);
        ocrEntityInsert.setResult(objectMapper.writeValueAsString(recognizeResultList));
        ocrEntityInsert.setCreateTime(new Date());
        ocrDao.insertOcr(ocrEntityInsert);

        return recognizeResultList;
    }

前端

<!--原理是原图片作为背景图片,然后把识别的文字叠加到图片上,看起来就是被识别的样式-->
<template>
  <a-modal
      :title="title"
      :dialog-style="{ top: '5px' }"
      :visible="visible"
      :width="1200"
      :destroyOnClose="true"
      @ok="handleOk"
      :footer="null"
      @cancel="handleCancel">
    <div style="height: 650px;overflow: hidden;display: flex">
      <div :style="{width: width+'px',height: height+'px'}">
        <canvas id="canvas" ref="canvas" @click="getText">
        </canvas>
      </div>
      <div style="border-left: 1px solid lightgrey">
        <div style="font-size: 16px;font-weight: bold;margin-left: 5px">
          识别结果
        </div>
        <div id="textList" style="height: 630px;width: 250px;overflow-y: auto;">
          <div class="textClass" @click="copyText(item.words)" :style="{backgroundColor: currentId===item.id?'rgb(232, 240, 254)':'white' }" v-for="(item) in data" style="" :id="item.id">
            {{item.words}}
          </div>
        </div>

      </div>
    </div>
  </a-modal>
</template>

<script>
import {v4 as UuidV4} from "uuid";
import Tiff from "tiff.js"
// noinspection JSUnusedLocalSymbols
export default {
  name: "DataSourceEdit",
  data(){
    return{
      title: "",
      visible: false,
      canvasId: UuidV4(),
      data: [],
      file: null,
      width: 930,
      height: 660,
      positionList: [],
      currentText: "",
      currentId: ""
    }
  },mounted() {
    Tiff.initialize({TOTAL_MEMORY :150 * 1024 * 1024});
  },methods:{
    show(data, file){
      this.canvasId = "";
      this.data = data;
      this.file = file;
      this.visible = true;
      this.$nextTick(()=>{
        this.createCanvas(data,file);
      })
    },
    handleOk(){
      this.visible = false;
    },
    handleCancel(){
      this.visible = false;
    },
    loadImageToViewer(buffer){
      let tiff;
      try {
        tiff = new Tiff({buffer: buffer})
      } catch (e) {
        this.$message.info("当前tiff无法查看!");
        return;
      }
      for (let i = 0, len = tiff.countDirectory(); i < len; ++i) {
        tiff.setDirectory(i)
        const imgs = tiff.toDataURL(); // 转化成base64
        if (imgs) {
          this.canvasList.push(imgs)
        }
      }
      this.$nextTick(() => {
        const viewer = this.$refs['viewer'].$viewer
        viewer.show()
      })
    },
    //创建canvas文件,其中tiff文件的背景图片加载要单独处理
    createCanvas(data, file){
      const that = this;
      //tiff文件特殊处理
      if(file.name.toLowerCase().indexOf(".tif")>-1 || file.name.toLowerCase().indexOf(".tiff")>-1 ){
        const bufferReader = new FileReader()
        bufferReader.readAsArrayBuffer(file)
        bufferReader.onload = () => {
          if (bufferReader && bufferReader.result) {
            const image = new Tiff({ buffer: bufferReader.result })
            that.loadCanvas(image.toDataURL('image/jpeg'), data, file)
          } else {
            cosole.error('上传失败, 请重试')
          }
        }
        return;
      }
      const URL = window.URL || window.webkitURL;
      // 通过 file 生成目标 url
      const imgURL = URL.createObjectURL(file);
      this.loadCanvas(imgURL, data, file);
    },
    loadCanvas(imgURL, data, file){
      let image = new Image()
      image.src = imgURL;
      const canvas = this.$refs.canvas;
      const ctx = canvas.getContext('2d');
      image.onload =()=>{

        //因为有些图片很大,有些图片很小,所以这里在显示图片的时候,都是按照屏幕自适应
        //自适应以后,图片的位置就发生了变化,原来后台识别的文字坐标都是按照图片决定定位的,所以在绘制文字框的时候,也需要随着图片的缩放,自动调整位置和大小
        canvas.width = image.width;
        canvas.height = image.height;
        let scaleX = Math.floor((this.width/image.width) * 100) /100;
        if(this.width>image.width){
          scaleX = 1;
        }
        let scaleY = Math.floor((this.height/image.height) * 100) / 100
        if(this.height>image.height){
          scaleY=1;
        }
        ctx.scale(scaleX,scaleY)
        ctx.textBaseline = "top";
        ctx.drawImage(image, 0, 0, image.width, image.height);
        ctx.fillStyle = "rgba(24,144,255,0.3)"
        data.forEach(p=>{
          ctx.lineWidth = 1;
          ctx.strokeStyle = "red";
          ctx.fillRect(p.location.left, p.location.top, p.location.width, p.location.height);
          ctx.strokeRect(p.location.left, p.location.top, p.location.width, p.location.height);
          //记录坐标列表,文字所在的框,方便后续和文字对应
          this.positionList.push({
            x: p.location.left * scaleX,
            y: p.location.top * scaleY,
            xEnd: (p.location.left + p.location.width) * scaleX,
            yEnd: (p.location.top + p.location.height) * scaleY,
            text: p.words,
            id: p.id
          })
        })
        console.log(data)
      }
    },
    /**
     * 这里做了个小效果,点击图片上的文字的时候,右边的列表会自动定位,便于复制
     */
    getText(e){
      console.log(e)
      const canvas = this.$refs.canvas;
      const clickX = e.pageX - canvas.offsetLeft;
      const clickY = e.pageY - canvas.offsetTop;
      const filterData = this.positionList.filter(p =>
          clickX >= p.x && clickY >= p.y && clickX <= p.xEnd && clickY <= p.yEnd
      );
      if(filterData.length > 0){
        const divList = document.getElementById("textList");
        const divText = document.getElementById(filterData[0].id);
        divList.scrollTop = divText.offsetTop -50;
        this.currentId = filterData[0].id
      }
    },
    copyText(words){
      this.$copyText(words).then(
          () => {
            this.$message.info("复制成功!");
          },
          () => {
            this.$message.error("复制失败了");
          }
      );
    }
  }
}
</script>

<style scoped>
.textClass{
  border-bottom: 1px solid lightgrey;cursor: pointer;padding:5px
}
.textClass:hover{
  background-color: rgb(232, 240, 254);
}
</style>