Instantly share code, notes, and snippets.

@kibotu /Defish.kt
Last active Jul 20, 2018

Embed
What would you like to do?
DeFish
/**
*
input:
strength as floating point >= 0. 0 = no change, high numbers equal stronger correction.
zoom as floating point >= 1. (1 = no change in zoom)
algorithm:
set halfWidth = imageWidth / 2
set halfHeight = imageHeight / 2
if strength = 0 then strength = 0.00001
set correctionRadius = squareroot(imageWidth ^ 2 + imageHeight ^ 2) / strength
for each pixel (x,y) in destinationImage
set newX = x - halfWidth
set newY = y - halfHeight
set distance = squareroot(newX ^ 2 + newY ^ 2)
set r = distance / correctionRadius
if r = 0 then
set theta = 1
else
set theta = arctangent(r) / r
set sourceX = halfWidth + theta * newX * zoom
set sourceY = halfHeight + theta * newY * zoom
set color of pixel (x, y) to color of source image pixel at (sourceX, sourceY)
*/
fun RemoveFishEye(bitmap: Bitmap, strength: Double): Bitmap {
Log.v("Defishing", "start...")
val start = System.currentTimeMillis()
val conf = Bitmap.Config.ARGB_8888 // see other conf types
val correctedImage = Bitmap.createBitmap(bitmap.width, bitmap.height, conf) // this creates a MUTABLE bitmap
//The center points of the image
val xc = bitmap.width / 2.0
val yc = bitmap.height / 2.0
var s = strength
if (strength == 0.0)
s = 0.00001
val correctionRadius = Math.sqrt((correctedImage.width * correctedImage.width + correctedImage.height * correctedImage.height).toDouble()) / s
var theta: Double
val zoom = 1.0
val src = DrawableBitmapContainer(bitmap)
val dst = DrawableBitmapContainer(correctedImage)
for (x in 0 until correctedImage.width) {
for (y in 0 until correctedImage.height) {
val newX = x - xc
val newY = y - yc
val distance = Math.sqrt(newX * newX + newY * newY)
val r = distance / correctionRadius
theta = if (r == 0.0)
1.0
else
Math.atan(r) / r
val sourceX = xc + theta * newX * zoom
val sourceY = yc + theta * newY * zoom
val xd = Math.max(0.0, Math.min(sourceX, (correctedImage.width - 1).toDouble())).toInt()
val yd = Math.max(0.0, Math.min(sourceY, (correctedImage.height - 1).toDouble())).toInt()
dst.setPixel(x, y, src.getPixel(xd, yd))
}
}
Log.v("Defishing", "done after " + (System.currentTimeMillis() - start) + " ms")
return dst.bimap
}
class DrawableBitmapContainer(private val image: Bitmap) {
private val width: Int = image.width
private val height: Int = image.height
private val pixels: IntArray
val bimap: Bitmap
get() {
image.setPixels(pixels, 0, width, 0, 0, width, height)
return image
}
init {
pixels = IntArray(width * height)
image.getPixels(pixels, 0, width, 0, 0, width, height)
}
fun getPixel(x: Int, y: Int): Int {
return pixels[x + y * width]
}
fun setPixel(x: Int, y: Int, color: Int) {
pixels[x + y * width] = color
}
}
@kibotu

This comment has been minimized.

Show comment
Hide comment
@kibotu

kibotu Jul 18, 2018

imageView.setImageBitmap(RemoveFishEye(bitmap, 3.5))

Owner

kibotu commented Jul 18, 2018

imageView.setImageBitmap(RemoveFishEye(bitmap, 3.5))

@kibotu

This comment has been minimized.

Show comment
Hide comment
@kibotu
Owner

kibotu commented Jul 18, 2018

e.g.:

@kibotu

This comment has been minimized.

Show comment
Hide comment
@kibotu

kibotu Jul 19, 2018

attribute vec4 position;
attribute vec2 texcoord;
varying vec2 texcoordVarying;

void main() {
    gl_Position = position;
    texcoordVarying = texcoord;
}
#ifdef GL_ES
precision highp float;
#endif

varying vec2 texcoordVarying;  // position received from vertex shader
uniform sampler2D texture;

uniform float time;
uniform vec2 textureSize;
uniform float alpha;
uniform float strength;
uniform float zoom;

void main(void) {

    // correction radius
    float cr = sqrt(textureSize.x * textureSize.x + textureSize.y * textureSize.y) / strength;

    // half width
    float hW = textureSize.x / 2.0;

    // half height
    float hH = textureSize.y / 2.0;

    // translate uv by half resolution
    vec2 translatedUv = vec2(
        texcoordVarying.x * textureSize.x - hW,
        texcoordVarying.y * textureSize.y -  hH
    );

    // distance
    float dis = sqrt((translatedUv.x) *
                     (translatedUv.x)
		            +(translatedUv.y) *
                     (translatedUv.y));

    // distance ratio to radius
    float r = dis / cr;

    // avoid null pointer
    float theta = r == 0.0
        ? 1.0
        // arc tangens by corrected distance
        : atan(r) / r;

    // get source coordinate by distance correction and apply zooming
    float sX = hW + theta * translatedUv.x * zoom;
    float sY = hH + theta * translatedUv.y * zoom;

    // use new uv coordinates
    vec2 newUv = vec2(sX /textureSize.x , sY / textureSize.y);
    vec4 color = texture2D(texture, newUv);

    gl_FragColor = vec4(color.r, color.g, color.b, alpha);
}
Owner

kibotu commented Jul 19, 2018

attribute vec4 position;
attribute vec2 texcoord;
varying vec2 texcoordVarying;

void main() {
    gl_Position = position;
    texcoordVarying = texcoord;
}
#ifdef GL_ES
precision highp float;
#endif

varying vec2 texcoordVarying;  // position received from vertex shader
uniform sampler2D texture;

uniform float time;
uniform vec2 textureSize;
uniform float alpha;
uniform float strength;
uniform float zoom;

void main(void) {

    // correction radius
    float cr = sqrt(textureSize.x * textureSize.x + textureSize.y * textureSize.y) / strength;

    // half width
    float hW = textureSize.x / 2.0;

    // half height
    float hH = textureSize.y / 2.0;

    // translate uv by half resolution
    vec2 translatedUv = vec2(
        texcoordVarying.x * textureSize.x - hW,
        texcoordVarying.y * textureSize.y -  hH
    );

    // distance
    float dis = sqrt((translatedUv.x) *
                     (translatedUv.x)
		            +(translatedUv.y) *
                     (translatedUv.y));

    // distance ratio to radius
    float r = dis / cr;

    // avoid null pointer
    float theta = r == 0.0
        ? 1.0
        // arc tangens by corrected distance
        : atan(r) / r;

    // get source coordinate by distance correction and apply zooming
    float sX = hW + theta * translatedUv.x * zoom;
    float sY = hH + theta * translatedUv.y * zoom;

    // use new uv coordinates
    vec2 newUv = vec2(sX /textureSize.x , sY / textureSize.y);
    vec4 color = texture2D(texture, newUv);

    gl_FragColor = vec4(color.r, color.g, color.b, alpha);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment