更新记录

1.0.8(2026-03-31)

  • 删除ios资源,减少包大小

1.0.7(2026-03-31)

  • 优化Android扫描
  • 优化扫描界面
  • 破坏性更新showLPRScanner,支持返回车牌照片

1.0.6(2026-03-16)

  • 优化iOS扫码时间过长
查看更多

平台兼容性

uni-app(4.87)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-vue插件版本 app-nvue Android Android插件版本 iOS iOS插件版本 鸿蒙
1.0.0 1.0.0 × × 1.0.0 × 5.0 1.0.0 12 1.0.0 ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - × ×

uni-app x(4.87)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 微信小程序
× × 5.0 1.0.0 12 1.0.0 × ×

sn-uts-hyperlpr3

一个基于 UTS 开发的车牌识别插件,支持 Android 和 iOS 平台,提供相机扫描和图片识别两种方式识别车牌号码。

UTS环境兼容性

uni-app uni-app x

功能特性

  • ✅ 支持相机实时扫描识别车牌
  • ✅ 支持从图片文件识别车牌
  • ✅ 识别结果包含车牌号码、类型和置信度
  • ✅ 支持多种车牌类型识别
  • ✅ 跨平台支持(Android、iOS)

版本变更(重要)

  • 1.0.7 破坏性修改showLPRScanner 增加 options 参数。旧写法 showLPRScanner(callback) 请改为 showLPRScanner({}, callback)

安装使用

1. 导入插件

uni_modules 目录下已包含插件,无需额外安装。

2. 在页面中使用

<template>
  <view class="page-container">
    <button @click="openScanner">打开扫描器</button>
    <button @click="recognizeFromImage">识别图片</button>
  </view>
</template>

<script>
import { showLPRScanner, recognizeImage } from '@/uni_modules/sn-uts-hyperlpr3'

export default {
  methods: {
    openScanner() {
      showLPRScanner({}, (result) => {
        if (result.code === 0) {
          console.log('识别成功:', result.data.plateNumber)
        } else {
          console.error('识别失败:', result.data.error)
        }
      })
    },

    recognizeFromImage() {
      const imagePath = '/static/chepai/test1.png'
      recognizeImage(imagePath, (result) => {
        if (result.code === 0) {
          console.log('识别成功:', result.data.plateNumber)
        } else {
          console.error('识别失败:', result.data.error)
        }
      })
    }
  }
}
</script>

API 文档

函数 API

showLPRScanner

打开相机扫描器进行实时车牌识别。

  • 参数:
    • options: SnUtsHyperlpr3Options - 配置项(可传 {}
    • images?: { back?: string, flash?: string } - 自定义按钮图片(静态资源路径或本地路径)
    • callback: SnUtsHyperlpr3Callback - 识别结果回调函数
  • 返回: void

回调结果类型 SnUtsHyperlpr3Result:

{
  code: number,        // 0 表示成功,-1 表示失败
  data: {
    plateNumber?: string,  // 车牌号码
    plateType?: string,    // 车牌类型(中文描述)
    type?: number,          // 车牌类型(数字代码)
    confidence?: number     // 置信度(0-1)
    error?: string          // 错误信息(失败时)
    rotation?: number       // 旋转角度  // 1.0.7+
    imagePath?: string      // 车牌图片  // 1.0.7+
    fullImagePath?: string  // 原图路径  // 1.0.7+
  }
}

示例

<script>
import { showLPRScanner } from '@/uni_modules/sn-uts-hyperlpr3'

export default {
  methods: {
    handleScan() {
      showLPRScanner({}, (result) => {
        if (result.code === 0) {
          console.log('车牌号码:', result.data.plateNumber)
          console.log('车牌类型:', result.data.plateType)
          console.log('置信度:', result.data.confidence)
        } else {
          console.error('识别失败:', result.data.error)
        }
      })
    }
  }
}
</script>

自定义按钮图片示例

<script>
import { showLPRScanner } from '@/uni_modules/sn-uts-hyperlpr3'

export default {
  methods: {
    handleScan() {
      showLPRScanner({
        images: {
          back: '/static/icon_back.png',
          flash: '/static/icon_flash.png'
        }
      }, (result) => {
        console.log(result)
      })
    }
  }
}
</script>

recognizeImage

从图片文件识别车牌。

  • 参数:
    • imagePath: string - 图片文件路径(支持本地路径和绝对路径)
    • callback: SnUtsHyperlpr3Callback - 识别结果回调函数
  • 返回: void

图片路径说明

  • 本地资源路径:/static/chepai/test1.png
  • App 平台绝对路径:需要使用 plus.io.convertLocalFileSystemURL() 转换

示例

<script>
import { recognizeImage } from '@/uni_modules/sn-uts-hyperlpr3'

export default {
  methods: {
    handleRecognize() {
      // 获取图片的绝对路径
      let imagePath = '/static/chepai/test1.png'
      // #ifdef APP-PLUS
      imagePath = plus.io.convertLocalFileSystemURL('/static/chepai/test1.png')
      // #endif

      recognizeImage(imagePath, (result) => {
        if (result.code === 0) {
          console.log('识别成功:', result.data.plateNumber)
        } else {
          console.error('识别失败:', result.data.error)
        }
      })
    }
  }
}
</script>

使用示例

基础示例

<template>
    <scroll-view scroll-y class="scroll-content">
        <view class="content">
            <!-- 测试图片预览 -->
            <view class="image-section">
                <text class="section-title">测试图片</text>
                <view class="test-tabs">
                    <view class="test-tab" :class="{ active: currentTestIndex === 0 }" @click="currentTestIndex = 0">
                        <text>test1</text>
                    </view>
                    <view class="test-tab" :class="{ active: currentTestIndex === 1 }" @click="currentTestIndex = 1">
                        <text>test2</text>
                    </view>
                </view>
                <image class="test-image" :src="testImages[currentTestIndex]" mode="aspectFit" @click="previewImage">
                </image>
            </view>

            <!-- 识别结果 -->
            <view class="result-section" v-if="result">
                <text class="section-title">识别结果</text>
                <view class="result-card">
                    <view class="result-item" v-if="result.plateNumber">
                        <text class="result-label">车牌号码:</text>
                        <text class="result-value">{{ result.plateNumber }}</text>
                    </view>
                    <view class="result-item" v-if="result.plateType">
                        <text class="result-label">车牌类型:</text>
                        <text class="result-value">{{ result.plateType }}</text>
                    </view>
                    <view class="result-item" v-if="result.confidence">
                        <text class="result-label">置信度:</text>
                        <text class="result-value">{{ (result.confidence * 100).toFixed(2) }}%</text>
                    </view>
                    <view class="result-item" v-if="result.rotation !== undefined">
                        <text class="result-label">旋转角度:</text>
                        <text class="result-value">{{ result.rotation }}°</text>
                    </view>
                    <view class="result-item" v-if="result.imagePath">
                        <text class="result-label">图片路径:</text>
                        <text class="result-value result-path">{{ result.imagePath }}</text>
                    </view>
                    <view class="captured-image-wrapper" v-if="result.imagePath">
                        <image class="captured-image" :src="'file://' + result.imagePath" mode="aspectFit"></image>
                    </view>
                    <view class="result-item" v-if="result.fullImagePath">
                        <text class="result-label">原图路径:</text>
                        <text class="result-value result-path">{{ result.fullImagePath }}</text>
                    </view>
                    <view class="captured-image-wrapper" v-if="result.fullImagePath">
                        <image class="captured-image" :src="'file://' + result.fullImagePath" mode="aspectFit"></image>
                    </view>
                </view>
            </view>

            <!-- 操作按钮 -->
            <view class="button-section">
                <button class="action-button primary" @click="openScanner">
                    <text class="button-icon">📷</text>
                    <text class="button-text">打开扫描器</text>
                </button>
                <button class="action-button" @click="recognizeFromImage">
                    <text class="button-icon">🖼️</text>
                    <text class="button-text">识别测试图片</text>
                </button>
            </view>
        </view>
    </scroll-view>
</template>

<script>
    import {
        showLPRScanner,
        recognizeImage
    } from '@/uni_modules/sn-uts-hyperlpr3'

    export default {
        data() {
            return {
                result: null,
                testImages: ['/static/chepai/test1.png', '/static/chepai/test3.png'],
                currentTestIndex: 0
            }
        },
        computed: {
            currentTestImage() {
                return this.testImages[this.currentTestIndex] || this.testImages[0]
            }
        },
        onLoad() {

        },
        methods: {
            // 打开扫描器
            openScanner() {
                // uni.showLoading({
                //  title: '准备中...'
                // })

                showLPRScanner({
                    // images: {
                        // back: '/static/icon_back.png',
                        // flash: '/static/icon_flash.png'
                    // }
                }, (result) => {
                    // uni.hideLoading()
                    console.log('showLPRScanner', result)
                    if (result.code === 0) {
                        this.result = result.data
                        uni.showToast({
                            title: '识别成功',
                            icon: 'success'
                        })
                    } else {
                        const errorMsg = result.data?.error || '识别失败'
                        uni.showToast({
                            title: errorMsg,
                            icon: 'none'
                        })
                        this.result = null
                    }
                })
            },

            // 识别图片
            recognizeFromImage() {
                uni.showLoading({
                    title: '识别中...'
                })

                let imagePath = this.currentTestImage
                // #ifdef APP-PLUS
                imagePath = plus.io.convertLocalFileSystemURL(this.currentTestImage)
                // #endif

                recognizeImage(imagePath, (result) => {
                    uni.hideLoading()

                    if (result.code === 0) {
                        this.result = result.data
                        uni.showToast({
                            title: '识别成功',
                            icon: 'success'
                        })
                    } else {
                        const errorMsg = result.data?.error || '识别失败'
                        uni.showToast({
                            title: errorMsg,
                            icon: 'none'
                        })
                        this.result = null
                    }
                })
            },

            // 预览图片
            previewImage() {
                uni.previewImage({
                    urls: this.testImages,
                    current: this.currentTestImage
                })
            }
        }
    }
</script>

<style scoped>
    .scroll-content {
        flex: 1;
        background-color: #f5f5f5;
    }

    .content {
        padding: 40rpx;
    }

    .image-section {
        background: #ffffff;
        border-radius: 24rpx;
        padding: 40rpx;
        margin-bottom: 40rpx;
        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
    }

    .section-title {
        display: block;
        font-size: 32rpx;
        font-weight: 600;
        color: #333333;
        margin-bottom: 30rpx;
    }

    .test-tabs {
        display: flex;
        flex-direction: row;
        gap: 20rpx;
        margin-bottom: 24rpx;
    }

    .test-tab {
        padding: 16rpx 32rpx;
        border-radius: 12rpx;
        background: #f0f0f0;
        font-size: 28rpx;
        color: #666666;
    }

    .test-tab.active {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: #ffffff;
    }

    .test-image {
        width: 100%;
        max-height: 500rpx;
        border-radius: 16rpx;
        background: #f8f8f8;
    }

    .result-section {
        background: #ffffff;
        border-radius: 24rpx;
        padding: 40rpx;
        margin-bottom: 40rpx;
        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
    }

    .result-card {
        background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
        border-radius: 16rpx;
        padding: 30rpx;
    }

    .result-item {
        display: flex;
        align-items: center;
        margin-bottom: 24rpx;
    }

    .result-item:last-child {
        margin-bottom: 0;
    }

    .result-label {
        font-size: 28rpx;
        color: #666666;
        margin-right: 16rpx;
        min-width: 140rpx;
    }

    .result-value {
        font-size: 32rpx;
        font-weight: 600;
        color: #333333;
        flex: 1;
    }

    .result-path {
        font-size: 24rpx;
        word-break: break-all;
    }

    .captured-image-wrapper {
        margin-top: 20rpx;
        border-radius: 12rpx;
        overflow: hidden;
        background: #f0f0f0;
    }

    .captured-image {
        width: 100%;
        height: 400rpx;
    }

    .button-section {
        display: flex;
        flex-direction: column;
        gap: 24rpx;
    }

    .action-button {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 100rpx;
        border-radius: 16rpx;
        font-size: 32rpx;
        font-weight: 600;
        border: none;
        background: #ffffff;
        color: #333333;
        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
        transition: all 0.3s ease;
    }

    .action-button.primary {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: #ffffff;
        box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
    }

    .action-button:active {
        transform: scale(0.98);
        opacity: 0.9;
    }

    .button-icon {
        font-size: 40rpx;
        margin-right: 16rpx;
    }

    .button-text {
        font-size: 32rpx;
    }
</style>

批量识别图片

<template>
  <view class="page-container">
    <button @click="recognizeBatch">批量识别</button>
    <view v-for="(item, index) in results" :key="index">
      <text>{{ item.path }}: {{ item.result?.plateNumber || '识别失败' }}</text>
    </view>
  </view>
</template>

<script>
import { recognizeImage } from '@/uni_modules/sn-uts-hyperlpr3'

export default {
  data() {
    return {
      imagePaths: [
        '/static/chepai/test1.png',
        '/static/chepai/test2.png'
      ],
      results: []
    }
  },
  methods: {
    recognizeBatch() {
      this.results = []

      this.imagePaths.forEach((path, index) => {
        let imagePath = path
        // #ifdef APP-PLUS
        imagePath = plus.io.convertLocalFileSystemURL(path)
        // #endif

        recognizeImage(imagePath, (result) => {
          this.results.push({
            path: path,
            result: result.code === 0 ? result.data : null
          })
        })
      })
    }
  }
}
</script>

注意事项

1. 平台差异

  • 相机权限:使用 showLPRScanner 需要相机权限,首次使用时会自动请求
  • 图片路径:App 平台需要使用 plus.io.convertLocalFileSystemURL() 转换路径为绝对路径
  • 识别精度:识别精度受图片质量、光线条件、车牌清晰度等因素影响

2. 使用限制

  • 网络要求:识别功能为本地识别,无需网络连接
  • 图片格式:支持常见图片格式(JPG、PNG 等)
  • 图片大小:建议图片大小不超过 10MB,过大的图片可能影响识别速度

3. 性能优化

  • 识别速度:相机实时识别速度较快,图片识别速度取决于图片大小
  • 内存管理:识别完成后会自动释放资源,无需手动清理
  • 批量识别:建议控制并发数量,避免内存占用过高

4. 识别结果

  • 置信度:置信度范围 0-1,数值越高表示识别结果越可靠
  • 车牌类型:支持多种车牌类型,包括普通车牌、新能源车牌等
  • 错误处理:识别失败时,code 为 -1,错误信息在 data.error

5. 常见问题

Q: 为什么识别失败?

A: 可能的原因:

  • 图片中车牌不清晰或角度不正
  • 光线条件不佳
  • 车牌被遮挡
  • 图片格式不支持

Q: 如何提高识别准确率?

A: 建议:

  • 确保车牌清晰可见
  • 保持合适的拍摄距离和角度
  • 光线充足的环境
  • 避免反光和阴影

Q: App 平台图片路径如何获取?

A: 使用 plus.io.convertLocalFileSystemURL() 转换相对路径为绝对路径:

// #ifdef APP-PLUS
const absolutePath = plus.io.convertLocalFileSystemURL('/static/chepai/test1.png')
// #endif

Q: 相机扫描器如何关闭?

A: 识别成功或失败后,扫描器会自动关闭。用户也可以手动点击返回按钮关闭。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。