Skip to content

Instantly share code, notes, and snippets.

@ZacSweers
Last active July 6, 2024 16:36
Show Gist options
  • Select an option

  • Save ZacSweers/aac5f2e684e125e64ae6b1ad0a2540aa to your computer and use it in GitHub Desktop.

Select an option

Save ZacSweers/aac5f2e684e125e64ae6b1ad0a2540aa to your computer and use it in GitHub Desktop.

Revisions

  1. ZacSweers revised this gist Dec 10, 2018. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion BlurrinessDetection.kt
    Original file line number Diff line number Diff line change
    @@ -121,7 +121,8 @@ object BlurrinessDetection {

    @ColorInt val mostLuminousColor = mostLuminousColorFromBitmap(edgesBitmap)
    val colorHex = "#" + Integer.toHexString(mostLuminousColor)
    val isBlurry = mostLuminousColor < Color.parseColor("#CECECE") // Demo threshold
    val isBlurry = mostLuminousColor > Color.parseColor("#CECECE") // Demo threshold
    // Note - in Android, Color.BLACK is -16777216 and Color.WHITE is -1, so range is somewhere in between. Higher is more luminous
    Toast.makeText(context,
    "Most luminous color is $colorHex. Blurry = $isBlurry",
    Toast.LENGTH_SHORT
  2. ZacSweers revised this gist Dec 10, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion BlurrinessDetection.kt
    Original file line number Diff line number Diff line change
    @@ -121,7 +121,7 @@ object BlurrinessDetection {

    @ColorInt val mostLuminousColor = mostLuminousColorFromBitmap(edgesBitmap)
    val colorHex = "#" + Integer.toHexString(mostLuminousColor)
    val isBlurry = mostLuminousColor > Color.parseColor("#CECECE") // Demo threshold
    val isBlurry = mostLuminousColor < Color.parseColor("#CECECE") // Demo threshold
    Toast.makeText(context,
    "Most luminous color is $colorHex. Blurry = $isBlurry",
    Toast.LENGTH_SHORT
  3. ZacSweers created this gist Dec 9, 2018.
    154 changes: 154 additions & 0 deletions BlurrinessDetection.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,154 @@
    /*
    * Copyright (c) 2018. Uber Technologies
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    package com.uber.blurdetectiondemo

    import android.content.Context
    import android.graphics.Bitmap
    import android.graphics.Color
    import android.renderscript.Allocation
    import android.renderscript.Element
    import android.renderscript.RenderScript
    import android.renderscript.ScriptIntrinsicBlur
    import android.renderscript.ScriptIntrinsicColorMatrix
    import android.renderscript.ScriptIntrinsicConvolve3x3
    import android.widget.Toast
    import androidx.annotation.ColorInt

    object BlurrinessDetection {

    private val CLASSIC_MATRIX = floatArrayOf(
    -1.0f, -1.0f, -1.0f,
    -1.0f, 8.0f, -1.0f,
    -1.0f, -1.0f, -1.0f
    )

    /**
    * This attempts to determine if a given [sourceBitmap] is blurry using a combination of
    * renderscript utilities.
    *
    * Operations go like this:
    * - Soft blur
    * - Greyscale
    * - 3x3 convolution
    *
    * @return if the source bitmap is "blurry"
    */
    fun runDetection(context: Context, sourceBitmap: Bitmap): Boolean {
    val rs = RenderScript.create(context)

    // First apply a soft blur to smoothen out visual artifacts
    val smootherBitmap = Bitmap.createBitmap(sourceBitmap.width,
    sourceBitmap.height,
    sourceBitmap.config
    )
    val blurIntrinsic = ScriptIntrinsicBlur.create(rs, Element.RGBA_8888(rs))
    val source = Allocation.createFromBitmap(rs,
    sourceBitmap,
    Allocation.MipmapControl.MIPMAP_NONE,
    Allocation.USAGE_SHARED
    )
    val blurTargetAllocation = Allocation.createFromBitmap(rs,
    smootherBitmap,
    Allocation.MipmapControl.MIPMAP_NONE,
    Allocation.USAGE_SHARED
    )
    blurIntrinsic.apply {
    setRadius(1f)
    setInput(source)
    forEach(blurTargetAllocation)
    }
    blurTargetAllocation.copyTo(smootherBitmap)

    // Greyscale so we're only dealing with white <--> black pixels, this is so we only need to
    // detect pixel
    // luminosity
    val greyscaleBitmap = Bitmap.createBitmap(sourceBitmap.width,
    sourceBitmap.height,
    sourceBitmap.config
    )
    val smootherInput = Allocation.createFromBitmap(rs,
    smootherBitmap,
    Allocation.MipmapControl.MIPMAP_NONE,
    Allocation.USAGE_SHARED
    )
    val greyscaleTargetAllocation = Allocation.createFromBitmap(rs,
    greyscaleBitmap,
    Allocation.MipmapControl.MIPMAP_NONE,
    Allocation.USAGE_SHARED
    )

    // Inverts and greyscales the image
    val colorIntrinsic = ScriptIntrinsicColorMatrix.create(rs)
    colorIntrinsic.setGreyscale()
    colorIntrinsic.forEach(smootherInput, greyscaleTargetAllocation)
    greyscaleTargetAllocation.copyTo(greyscaleBitmap)

    // Run edge detection algorithm using a laplacian matrix convolution
    // Apply 3x3 convolution to detect edges
    val edgesBitmap = Bitmap.createBitmap(sourceBitmap.width,
    sourceBitmap.height,
    sourceBitmap.config
    )
    val greyscaleInput = Allocation.createFromBitmap(rs,
    greyscaleBitmap,
    Allocation.MipmapControl.MIPMAP_NONE,
    Allocation.USAGE_SHARED
    )
    val edgesTargetAllocation = Allocation.createFromBitmap(rs,
    edgesBitmap,
    Allocation.MipmapControl.MIPMAP_NONE,
    Allocation.USAGE_SHARED
    )

    val convolve = ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs))
    convolve.setInput(greyscaleInput)
    convolve.setCoefficients(CLASSIC_MATRIX) // Or use others
    convolve.forEach(edgesTargetAllocation)
    edgesTargetAllocation.copyTo(edgesBitmap)

    @ColorInt val mostLuminousColor = mostLuminousColorFromBitmap(edgesBitmap)
    val colorHex = "#" + Integer.toHexString(mostLuminousColor)
    val isBlurry = mostLuminousColor > Color.parseColor("#CECECE") // Demo threshold
    Toast.makeText(context,
    "Most luminous color is $colorHex. Blurry = $isBlurry",
    Toast.LENGTH_SHORT
    ).show()
    return isBlurry
    }

    /**
    * Resolves the most luminous color pixel in a given bitmap.
    *
    * @param bitmap Source bitmap.
    * @return The most luminous color pixel in the `bitmap`
    */
    @ColorInt
    fun mostLuminousColorFromBitmap(bitmap: Bitmap): Int {
    bitmap.setHasAlpha(false)
    val pixels = IntArray(bitmap.height * bitmap.width)
    bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)

    @ColorInt var mostLuminousColor = Color.BLACK

    for (pixel in pixels) {
    if (pixel > mostLuminousColor) {
    mostLuminousColor = pixel
    }
    }
    return mostLuminousColor
    }

    }