Преглед изворни кода

优化拍照识别算法,使用优化的快速清晰度检测方案

ZhangLeo пре 2 месеци
родитељ
комит
bf74d7ed48

+ 372 - 26
nativeplugins/opencv/android/uts/BitmapUtils.uts

@@ -178,55 +178,401 @@ export class BitmapUtils {
 	}
 	
 	/**
-	 * 使用边缘密度作为清晰度指标(更适合大图
+	 * 基于边缘梯度的清晰度检测(对模糊更敏感
 	 */
-	static calculateEdgeDensity(bitmap: Bitmap): number {
+	static calculateEdgeGradientClarity(bitmap: Bitmap): number {
 	    try {
 	        const width = bitmap.getWidth()
 	        const height = bitmap.getHeight()
 	        
-	        // 对于大图,使用适当的采样尺寸
-	        const sampleWidth = Math.min(width, 300)
-	        const sampleHeight = Math.min(height, (300 * height / width).toInt())
+	        // 取中心区域
+	        const centerRatio = 0.7
+	        const centerWidth = (width * centerRatio).toInt()
+	        const centerHeight = (height * centerRatio).toInt()
+	        const startX = ((width - centerWidth) / 2).toInt()
+	        const startY = ((height - centerHeight) / 2).toInt()
+	        
+	        const centerBitmap = Bitmap.createBitmap(
+	            bitmap,
+	            startX,
+	            startY,
+	            centerWidth,
+	            centerHeight
+	        )
+	        
+	        if (centerBitmap == null) {
+	            return -1
+	        }
+	        
+	        // 缩放以提高性能
+	        const sampleWidth = Math.min(centerWidth, 300)
+	        const sampleHeight = Math.min(centerHeight, (300 * centerHeight / centerWidth).toInt())
+	        
+	        const scaledBitmap = Bitmap.createScaledBitmap(
+	            centerBitmap,
+	            sampleWidth.toInt(),
+	            sampleHeight.toInt(),
+	            false
+	        )
+	        
+	        let totalGradient = 0.0
+	        let gradientCount = 0
+	        
+	        // 计算梯度(边缘强度)
+	        for (let i: number = 1; i < sampleWidth - 1; i++) {
+	            for (let j: number = 1; j < sampleHeight - 1; j++) {
+	                const iInt = i.toInt()
+	                const jInt = j.toInt()
+	                
+	                // 获取3x3区域的像素
+	                const p00 = this.getPixelBrightness(scaledBitmap.getPixel(iInt - 1, jInt - 1))
+	                const p01 = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt - 1))
+	                const p02 = this.getPixelBrightness(scaledBitmap.getPixel(iInt + 1, jInt - 1))
+	                const p10 = this.getPixelBrightness(scaledBitmap.getPixel(iInt - 1, jInt))
+	                const p12 = this.getPixelBrightness(scaledBitmap.getPixel(iInt + 1, jInt))
+	                const p20 = this.getPixelBrightness(scaledBitmap.getPixel(iInt - 1, jInt + 1))
+	                const p21 = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt + 1))
+	                const p22 = this.getPixelBrightness(scaledBitmap.getPixel(iInt + 1, jInt + 1))
+	                
+	                // 计算Sobel梯度
+	                const gx = (p02 + 2 * p12 + p22) - (p00 + 2 * p10 + p20)
+	                const gy = (p20 + 2 * p21 + p22) - (p00 + 2 * p01 + p02)
+	                const gradient = Math.sqrt(gx * gx + gy * gy)
+	                
+	                // 只考虑明显的边缘(避免噪声)
+	                if (gradient > 10) {
+	                    totalGradient += gradient
+	                    gradientCount++
+	                }
+	            }
+	        }
+	        
+	        const avgGradient = gradientCount > 0 ? totalGradient / gradientCount : 0
+	        
+	        // 回收Bitmap
+	        if (!scaledBitmap.isRecycled()) {
+	            scaledBitmap.recycle()
+	        }
+	        if (!centerBitmap.isRecycled()) {
+	            centerBitmap.recycle()
+	        }
+	        
+	        return avgGradient
+	        
+	    } catch (error) {
+	        console.error('边缘梯度清晰度计算失败:', error)
+	        return -1
+	    }
+	}
+	
+	/**
+	 * 快速拉普拉斯方差 - 对模糊敏感且计算效率高
+	 */
+	static calculateLaplacianVariance(bitmap: Bitmap): number {
+	    try {
+	        const width = bitmap.getWidth()
+	        const height = bitmap.getHeight()
+	        
+	        // 取中心区域,但使用更小的采样
+	        const centerRatio = 0.6
+	        const centerWidth = (width * centerRatio).toInt()
+	        const centerHeight = (height * centerRatio).toInt()
+	        const startX = ((width - centerWidth) / 2).toInt()
+	        const startY = ((height - centerHeight) / 2).toInt()
+	        
+	        const centerBitmap = Bitmap.createBitmap(
+	            bitmap,
+	            startX,
+	            startY,
+	            centerWidth,
+	            centerHeight
+	        )
+	        
+	        if (centerBitmap == null) {
+	            return -1
+	        }
+	        
+	        // 使用较小的采样尺寸
+	        const sampleWidth = Math.min(centerWidth, 150)
+	        const sampleHeight = Math.min(centerHeight, 100)
 	        
 	        const scaledBitmap = Bitmap.createScaledBitmap(
-	            bitmap, 
-	            sampleWidth.toInt(), 
-	            sampleHeight.toInt(), 
+	            centerBitmap,
+	            sampleWidth.toInt(),
+	            sampleHeight.toInt(),
 	            false
 	        )
 	        
-	        let edgeCount = 0
-	        const totalPixels = sampleWidth * sampleHeight
+	        let laplacianSum = 0.0
+	        let pixelCount = 0
 	        
-	        // 简单的边缘检测
+	        // 简化的拉普拉斯算子 - 只计算中心与四邻域的差异
 	        for (let i: number = 1; i < sampleWidth - 1; i++) {
 	            for (let j: number = 1; j < sampleHeight - 1; j++) {
-	                const center = this.getPixelBrightness(scaledBitmap.getPixel(i.toInt(), j.toInt()))
-	                const right = this.getPixelBrightness(scaledBitmap.getPixel((i + 1).toInt(), j.toInt()))
-	                const bottom = this.getPixelBrightness(scaledBitmap.getPixel(i.toInt(), (j + 1).toInt()))
+	                const iInt = i.toInt()
+	                const jInt = j.toInt()
+	                
+	                const center = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt))
+	                const left = this.getPixelBrightness(scaledBitmap.getPixel(iInt - 1, jInt))
+	                const right = this.getPixelBrightness(scaledBitmap.getPixel(iInt + 1, jInt))
+	                const top = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt - 1))
+	                const bottom = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt + 1))
 	                
-	                // 如果相邻像素亮度差异大,认为是边缘
-	                if (Math.abs(center - right) > 20 || Math.abs(center - bottom) > 20) {
-	                    edgeCount++
+	                // 拉普拉斯值 = 4*中心 - 上下左右
+	                const laplacianValue = Math.abs(4 * center - left - right - top - bottom)
+	                
+	                if (laplacianValue > 5) { // 忽略微小变化
+	                    laplacianSum += laplacianValue
+	                    pixelCount++
 	                }
 	            }
 	        }
 	        
-	        const edgeDensity = (edgeCount / totalPixels) * 1000
+	        const avgLaplacian = pixelCount > 0 ? laplacianSum / pixelCount : 0
 	        
+	        // 回收Bitmap
 	        if (!scaledBitmap.isRecycled()) {
 	            scaledBitmap.recycle()
 	        }
+	        if (!centerBitmap.isRecycled()) {
+	            centerBitmap.recycle()
+	        }
 	        
-	        return edgeDensity
+	        return avgLaplacian
 	        
 	    } catch (error) {
-	        console.error('边缘密度计算失败:', error)
+	        console.error('拉普拉斯方差计算失败:', error)
 	        return -1
 	    }
 	}
     
+	/**
+	 * 快速局部对比度检测 - 优化性能
+	 */
+	static calculateFastLocalContrast(bitmap: Bitmap): number {
+	    try {
+	        const width = bitmap.getWidth()
+	        const height = bitmap.getHeight()
+	        
+	        // 直接使用缩放后的整个图像,不裁剪中心区域
+	        const sampleWidth = Math.min(width, 200)
+	        const sampleHeight = Math.min(height, 150)
+	        
+	        const scaledBitmap = Bitmap.createScaledBitmap(
+	            bitmap,
+	            sampleWidth.toInt(),
+	            sampleHeight.toInt(),
+	            false
+	        )
+	        
+	        let contrastSum = 0.0
+	        let sampleCount = 0
+	        
+	        // 使用步长采样,减少计算量
+	        const step = 2
+	        
+	        for (let i: number = 1; i < sampleWidth - 1; i += step) {
+	            for (let j: number = 1; j < sampleHeight - 1; j += step) {
+	                const iInt = i.toInt()
+	                const jInt = j.toInt()
+	                
+	                // 只检查水平和垂直方向的对比度
+	                const center = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt))
+	                const left = this.getPixelBrightness(scaledBitmap.getPixel(iInt - 1, jInt))
+	                const right = this.getPixelBrightness(scaledBitmap.getPixel(iInt + 1, jInt))
+	                const top = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt - 1))
+	                const bottom = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt + 1))
+	                
+	                // 计算最大相邻对比度
+	                const horizontalContrast = Math.max(Math.abs(center - left), Math.abs(center - right))
+	                const verticalContrast = Math.max(Math.abs(center - top), Math.abs(center - bottom))
+	                const maxContrast = Math.max(horizontalContrast, verticalContrast)
+	                
+	                if (maxContrast > 15) { // 只考虑明显的对比度
+	                    contrastSum += maxContrast
+	                    sampleCount++
+	                }
+	            }
+	        }
+	        
+	        const avgContrast = sampleCount > 0 ? contrastSum / sampleCount : 0
+	        
+	        if (!scaledBitmap.isRecycled()) {
+	            scaledBitmap.recycle()
+	        }
+	        
+	        return avgContrast
+	        
+	    } catch (error) {
+	        console.error('快速局部对比度计算失败:', error)
+	        return -1
+	    }
+	}
+	
+	/**
+	 * 自适应阈值清晰度检测
+	 * 根据图像内容动态调整判断标准
+	 */
+	static calculateAdaptiveClarity(bitmap: Bitmap): number {
+	    try {
+	        const width = bitmap.getWidth()
+	        const height = bitmap.getHeight()
+	        
+	        // 使用小尺寸采样
+	        const sampleWidth = Math.min(width, 150)
+	        const sampleHeight = Math.min(height, 100)
+	        
+	        const scaledBitmap = Bitmap.createScaledBitmap(
+	            bitmap,
+	            sampleWidth.toInt(),
+	            sampleHeight.toInt(),
+	            false
+	        )
+	        
+	        // 第一步:分析图像的整体对比度特征
+	        let highContrastCount = 0
+	        let totalSamples = 0
+	        
+	        for (let i: number = 2; i < sampleWidth - 2; i += 2) {
+	            for (let j: number = 2; j < sampleHeight - 2; j += 2) {
+	                const iInt = i.toInt()
+	                const jInt = j.toInt()
+	                
+	                const center = this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt))
+	                const neighbors = [
+	                    this.getPixelBrightness(scaledBitmap.getPixel(iInt - 1, jInt)),
+	                    this.getPixelBrightness(scaledBitmap.getPixel(iInt + 1, jInt)),
+	                    this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt - 1)),
+	                    this.getPixelBrightness(scaledBitmap.getPixel(iInt, jInt + 1))
+	                ]
+	                
+	                // 检查是否有高对比度边缘
+	                for (const neighbor of neighbors) {
+	                    if (Math.abs(center - neighbor) > 25) {
+	                        highContrastCount++
+	                        break
+	                    }
+	                }
+	                
+	                totalSamples++
+	            }
+	        }
+	        
+	        const highContrastRatio = highContrastCount / totalSamples
+	        
+	        // 第二步:基于对比度特征调整清晰度评分
+	        let clarityScore: number
+	        
+	        if (highContrastRatio < 0.05) {
+	            // 低对比度图像 - 可能是模糊或单色
+	            clarityScore = highContrastRatio * 500
+	        } else if (highContrastRatio < 0.15) {
+	            // 中等对比度
+	            clarityScore = 25 + (highContrastRatio - 0.05) * 250
+	        } else {
+	            // 高对比度 - 可能是清晰图像
+	            clarityScore = 50 + (highContrastRatio - 0.15) * 200
+	        }
+	        
+	        if (!scaledBitmap.isRecycled()) {
+	            scaledBitmap.recycle()
+	        }
+	        
+	        return clarityScore
+	        
+	    } catch (error) {
+	        console.error('自适应清晰度计算失败:', error)
+	        return -1
+	    }
+	}
+	
+	
+	/**
+	 * 优化的快速清晰度检测方案
+	 * 平衡准确性和性能
+	 */
+	static calculateOptimizedClarity(bitmap: Bitmap): number {
+	    try {
+	        // 使用两种快速方法,权重偏向对模糊更敏感的方法
+	        const laplacianScore = this.calculateAdaptiveClarity(bitmap)
+	        const contrastScore = this.calculateFastLocalContrast(bitmap)
+	        
+	        // 如果两种方法都返回有效结果,取加权平均
+	        if (laplacianScore > 0 && contrastScore > 0) {
+	            // 自适应阈值清晰度检测
+	            return laplacianScore * 0.3 + contrastScore * 0.7
+	        }
+	        
+	        // 如果只有一种方法有效,返回该结果
+	        return Math.max(laplacianScore, contrastScore)
+	        
+	    } catch (error) {
+	        console.error('优化清晰度计算失败:', error)
+	        return -1
+	    }
+	}
+	
+	/**
+	 * 超快速清晰度检测(最低计算量)
+	 */
+	static calculateUltraFastClarity(bitmap: Bitmap): number {
+	    try {
+	        const width = bitmap.getWidth()
+	        const height = bitmap.getHeight()
+	        
+	        // 极小的采样尺寸
+	        const sampleWidth = 80
+	        const sampleHeight = 60
+	        
+	        const scaledBitmap = Bitmap.createScaledBitmap(
+	            bitmap,
+	            sampleWidth.toInt(),
+	            sampleHeight.toInt(),
+	            false
+	        )
+	        
+	        let edgeStrength = 0.0
+	        let sampleCount = 0
+	        
+	        // 极简的边缘检测 - 只检查少数关键点
+	        const checkPoints = [
+	            { x: 20, y: 15 }, { x: 40, y: 15 }, { x: 60, y: 15 },
+	            { x: 20, y: 30 }, { x: 40, y: 30 }, { x: 60, y: 30 },
+	            { x: 20, y: 45 }, { x: 40, y: 45 }, { x: 60, y: 45 }
+	        ]
+	        
+	        for (const point of checkPoints) {
+	            const x  = point.x as number
+	            const y  = point.y as number
+	            
+	            if (x >= 1 && x < sampleWidth - 1 && y >= 1 && y < sampleHeight - 1) {
+	                const center = this.getPixelBrightness(scaledBitmap.getPixel(x.toInt(), y.toInt()))
+	                const right = this.getPixelBrightness(scaledBitmap.getPixel(x.toInt() + 1, y.toInt()))
+	                const bottom = this.getPixelBrightness(scaledBitmap.getPixel(x.toInt(), y.toInt() + 1))
+	                
+	                const edgeValue = Math.abs(center - right) + Math.abs(center - bottom)
+	                
+	                if (edgeValue > 10) {
+	                    edgeStrength += edgeValue
+	                    sampleCount++
+	                }
+	            }
+	        }
+	        
+	        const avgEdgeStrength = sampleCount > 0 ? edgeStrength / sampleCount : 0
+	        
+	        if (!scaledBitmap.isRecycled()) {
+	            scaledBitmap.recycle()
+	        }
+	        
+	        return avgEdgeStrength
+	        
+	    } catch (error) {
+	        console.error('超快速清晰度计算失败:', error)
+	        return -1
+	    }
+	}
+	
     /**
      * 获取像素亮度
      */
@@ -241,7 +587,7 @@ export class BitmapUtils {
      * 判断图像是否清晰
      */
     static isImageClear(bitmap: Bitmap, threshold: number = 500): boolean {
-        const variance = this.calculateBrightnessVariance2(bitmap)
+        const variance = this.calculateOptimizedClarity(bitmap)
         return variance > threshold
     }
     
@@ -249,18 +595,18 @@ export class BitmapUtils {
      * 获取图像清晰度评分
      */
     static getImageClarityScore(bitmap: Bitmap): ImageClarityScore {
-        const variance = this.calculateBrightnessVariance2(bitmap)
+        const variance = this.calculateOptimizedClarity(bitmap)
         
         let level: string
         if (variance < 0) {
             level = "处理失败"
-        } else if (variance < 500) {
+        } else if (variance < 15) {
             level = "非常模糊"
-        } else if (variance < 2000) {
+        } else if (variance < 35) {
             level = "模糊"
-        } else if (variance < 3200) {
+        } else if (variance < 45) {
             level = "一般"
-        } else if (variance < 3500) {
+        } else if (variance < 60) {
             level = "清晰"
         } else {
             level = "非常清晰"

+ 1 - 1
pages/work/download/TaskCamera-scan-code.uvue

@@ -313,7 +313,7 @@
 						  score = result?.['score'] as number
 						  level = result?.['level'] as string
 					      uni.hideLoading();
-						  const customThreshold = 3500;
+						  const customThreshold = 40;
 						  const isClearWithCustomThreshold = score > customThreshold;
 						  if (!isClearWithCustomThreshold) {
 						  	// 不清晰的图片,提示用户

+ 1 - 1
pages/work/record/camera-scan-code.uvue

@@ -324,7 +324,7 @@
 								score = result?.['score'] as number
 								level = result?.['level'] as string
 								uni.hideLoading();
-								const customThreshold = 3500;
+								const customThreshold = 40;
 								const isClearWithCustomThreshold = score > customThreshold;
 								if (!isClearWithCustomThreshold) {
 									// 不清晰的图片,提示用户