Instantly share code, notes, and snippets.

# kibotu/Defish.md

Last active September 24, 2020 06:35
Show Gist options
• Save kibotu/0f842ee2c1b2524747ce6529f48b6f42 to your computer and use it in GitHub Desktop.
DeFish

Basic Algorythm

```/**
*
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)
*/```

in kotlin

```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
}
}

imageView.setImageBitmap(RemoveFishEye(bitmap, 3.5))

```

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

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

uniform sampler2D texture;

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

void main(void) {

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));

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);
}```