// nativeplugins/opencv/android/uts/BitmapUtils.uts import Bitmap from 'android.graphics.Bitmap' import Base64 from 'android.util.Base64' import BitmapFactory from 'android.graphics.BitmapFactory' // 定义类型接口 type ImageClarityScore = { score: number level: string } type ImageClarityResult = { score: number level: string isClear: boolean } /** * 图像清晰度检测工具类 */ export class BitmapUtils { /** * 计算图像的亮度方差作为清晰度指标 */ static calculateBrightnessVariance(bitmap: Bitmap): number { try { const width = bitmap.getWidth() const height = bitmap.getHeight() // 根据原始尺寸动态计算采样尺寸 // 目标采样区域的总像素数,控制在合理范围内 const targetPixelCount = 2500 // 50x50 = 2500像素 // 计算合适的采样尺寸,保持宽高比 let sampleWidth: number let sampleHeight: number if (width > height) { // 宽图 sampleWidth = Math.min(width, Math.sqrt(targetPixelCount * width / height).toInt()) sampleHeight = Math.min(height, (sampleWidth * height / width).toInt()) } else { // 高图或方图 sampleHeight = Math.min(height, Math.sqrt(targetPixelCount * height / width).toInt()) sampleWidth = Math.min(width, (sampleHeight * width / height).toInt()) } // 确保最小尺寸 sampleWidth = Math.max(sampleWidth, 32) sampleHeight = Math.max(sampleHeight, 32) const scaledBitmap = Bitmap.createScaledBitmap( bitmap, sampleWidth.toInt(), sampleHeight.toInt(), false ) let totalBrightness = 0.0 let brightnessSquares = 0.0 const pixelCount = sampleWidth * sampleHeight // 计算平均亮度 for (let i: number = 0; i < sampleWidth; i++) { for (let j: number = 0; j < sampleHeight; j++) { const pixel = scaledBitmap.getPixel(i.toInt(), j.toInt()) const brightness = this.getPixelBrightness(pixel) totalBrightness += brightness } } const meanBrightness = totalBrightness / pixelCount // 计算亮度方差 for (let i: number = 0; i < sampleWidth; i++) { for (let j: number = 0; j < sampleHeight; j++) { const pixel = scaledBitmap.getPixel(i.toInt(), j.toInt()) const brightness = this.getPixelBrightness(pixel) const diff = brightness - meanBrightness brightnessSquares += diff * diff } } const variance = brightnessSquares / pixelCount // 回收临时Bitmap if (!scaledBitmap.isRecycled()) { scaledBitmap.recycle() } return variance } catch (error) { console.error('清晰度计算失败:', error) return -1 } } // 在calculateBrightnessVariance方法中使用多尺度采样 static calculateBrightnessVariance2(bitmap: Bitmap): number { try { const width = bitmap.getWidth() const height = bitmap.getHeight() // 对2048×1536的图片,使用多个采样尺度 const scales = [ { width: 200, height: 150 }, // 约10%尺度 { width: 100, height: 75 }, // 约5%尺度 { width: 50, height: 38 } // 约2.5%尺度 ] let totalVariance = 0.0 let scaleCount = 0 for (const scale of scales) { const sampleWidth = Math.min(width, scale.width as number) const sampleHeight = Math.min(height, scale.height as number) if (sampleWidth < 32 || sampleHeight < 32) continue const scaledBitmap = Bitmap.createScaledBitmap( bitmap, sampleWidth.toInt(), sampleHeight.toInt(), false ) const variance = this.calculateVarianceForBitmap(scaledBitmap) totalVariance += variance scaleCount++ if (!scaledBitmap.isRecycled()) { scaledBitmap.recycle() } } return scaleCount > 0 ? totalVariance / scaleCount : -1 } catch (error) { console.error('多尺度清晰度计算失败:', error) return -1 } } // 提取方差计算为独立方法 private static calculateVarianceForBitmap(bitmap: Bitmap): number { const width = bitmap.getWidth() const height = bitmap.getHeight() let totalBrightness = 0.0 let brightnessSquares = 0.0 const pixelCount = width * height // 计算平均亮度 for (let i: number = 0; i < width; i++) { for (let j: number = 0; j < height; j++) { const pixel = bitmap.getPixel(i.toInt(), j.toInt()) const brightness = this.getPixelBrightness(pixel) totalBrightness += brightness } } const meanBrightness = totalBrightness / pixelCount // 计算亮度方差 for (let i: number = 0; i < width; i++) { for (let j: number = 0; j < height; j++) { const pixel = bitmap.getPixel(i.toInt(), j.toInt()) const brightness = this.getPixelBrightness(pixel) const diff = brightness - meanBrightness brightnessSquares += diff * diff } } return brightnessSquares / pixelCount } /** * 使用边缘密度作为清晰度指标(更适合大图) */ static calculateEdgeDensity(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 scaledBitmap = Bitmap.createScaledBitmap( bitmap, sampleWidth.toInt(), sampleHeight.toInt(), false ) let edgeCount = 0 const totalPixels = sampleWidth * sampleHeight // 简单的边缘检测 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())) // 如果相邻像素亮度差异大,认为是边缘 if (Math.abs(center - right) > 20 || Math.abs(center - bottom) > 20) { edgeCount++ } } } const edgeDensity = (edgeCount / totalPixels) * 1000 if (!scaledBitmap.isRecycled()) { scaledBitmap.recycle() } return edgeDensity } catch (error) { console.error('边缘密度计算失败:', error) return -1 } } /** * 获取像素亮度 */ private static getPixelBrightness(pixel: number): number { const r = (pixel >> 16) & 0xff const g = (pixel >> 8) & 0xff const b = pixel & 0xff return (r + g + b) / 3.0 } /** * 判断图像是否清晰 */ static isImageClear(bitmap: Bitmap, threshold: number = 500): boolean { const variance = this.calculateBrightnessVariance2(bitmap) return variance > threshold } /** * 获取图像清晰度评分 */ static getImageClarityScore(bitmap: Bitmap): ImageClarityScore { const variance = this.calculateBrightnessVariance2(bitmap) let level: string if (variance < 0) { level = "处理失败" } else if (variance < 500) { level = "非常模糊" } else if (variance < 2000) { level = "模糊" } else if (variance < 3200) { level = "一般" } else if (variance < 3500) { level = "清晰" } else { level = "非常清晰" } return { score: Math.round(variance * 100) / 100, level: level } } /** * 从Base64数据检测清晰度 */ static checkImageClarityFromBase64(base64Data: string): ImageClarityResult { try { const bitmap = this.base64ToBitmap(base64Data) if (bitmap == null) { return { score: -1, level: "加载失败", isClear: false } } const clarityInfo = this.getImageClarityScore(bitmap) const isClear = this.isImageClear(bitmap) if (!bitmap.isRecycled()) { bitmap.recycle() } return { score: clarityInfo.score, level: clarityInfo.level, isClear: isClear } } catch (error) { console.error('图像清晰度检测失败:', error) return { score: -1, level: "检测失败", isClear: false } } } /** * 从文件路径检测清晰度 */ static checkImageClarityFromPath(imagePath: string): ImageClarityResult { try { const bitmap = BitmapFactory.decodeFile(imagePath) if (bitmap == null) { return { score: -1, level: "加载失败", isClear: false } } const clarityInfo = this.getImageClarityScore(bitmap) const isClear = this.isImageClear(bitmap) if (!bitmap.isRecycled()) { bitmap.recycle() } return { score: clarityInfo.score, level: clarityInfo.level, isClear: isClear } } catch (error) { console.error('图像清晰度检测失败:', error) return { score: -1, level: "检测失败", isClear: false } } } /** * 简化的Base64转Bitmap方法 */ private static base64ToBitmap(base64Data: string): Bitmap | null { try { // 移除Base64前缀 let pureBase64 = base64Data if (base64Data.includes(',')) { pureBase64 = base64Data.split(',')[1] } // 解码Base64 const decodedBytes = Base64.decode(pureBase64, Base64.DEFAULT) // 获取字节数组长度 const bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size) return bitmap } catch (error) { console.error('Base64转Bitmap失败:', error) return null } } /** * 备用方法:直接返回清晰度分数(避免复杂对象类型) */ static getSimpleClarityScore(bitmap: Bitmap): number { return this.calculateBrightnessVariance(bitmap) } /** * 从Base64获取简单清晰度分数 */ static getSimpleClarityScoreFromBase64(base64Data: string): number { try { const bitmap = this.base64ToBitmap(base64Data) if (bitmap == null) { return -1 } const score = this.calculateBrightnessVariance(bitmap) if (!bitmap.isRecycled()) { bitmap.recycle() } return score } catch (error) { console.error('清晰度检测失败:', error) return -1 } } }