| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- // 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
- }
- }
- }
|