Funkcja wzmacniająca lub osłabiająca wartość wejściową

0

Do pewnego algorytmu potrzebuję zastosować funkcję, która na postawie wartości wejściowej wypluje odpowiednio zmodyfikowaną wartość wyjściową. Wejściem i wyjściem ma być liczba z zakresu <0,255>, czyli po prostu UInt8. Chodzi o to, że im bliżej minimum lub maksimum skali (0 lub 255), tym bardziej wartość wejściowa ma się ”zbliżyć” do minimum lub maksimum. Czyli inaczej pisząc, im bliżej wartości granicznej, tym wychylenie ma być większe, a im bliżej środka — 127 — tym mniejsze.

Nie wiem czy wam to coś powie, ale nabazgrałem wykres ilustrujący działanie takiej funkcji:

function.png

Chciałbym taką funkcję zastosować na składowej koloru (obraz w skali szarości), w algorytmie przypominającym wyostrzanie. W efekcie piksele o odcieniach zbliżonych do czarnego staną się czarne lub bardzo bliskie czerni (mocno osłabione), a te o odcieniach zbliżonych do bieli staną się białe lub bardzo bliskie bieli.

Co więcej, dobrze by było, gdyby taka funkcja pobierała na wejściu nie tylko wartość składowej koloru (wejściowy UInt8), ale też parametr określający stopień wzmocnienia/osłabienia — czyli inaczej pisząc, kształt wykresu. Np. im wyższa wartość tego parametru, tym bardziej wygięta krzywa. Dla dużej wartości parametru, wykres powinien być mocno wygięty, dla wartości centralnej średnio wygięty, a dla zerowej płaski, co będzie tożsame z brakiem modyfikacji wartości wyjściowej:

param.png

Niestety nie mam bladego pojęcia jak taką funkcję napisać — dupa ze mnie nie matematyk. Próbowałem zastosować nieparzystą funkcję potęgową i odpowiednio ją przerobić dla danego zakresu wartości, ale nic mi z tego nie wyszło.

Doradzicie jak się za to zabrać? Pomysły mi się skończyły… :D

1

Cotangens wygląda obiecująco:
https://www.wolframalpha.com/input/?i=cotan%28x%29

4

Krzywa b-sklejana lub jedna z funkcji aktywacyjnej sieci neuronowej ma taki kształt wystarczy modyfikowac x.

https://edu.pjwstk.edu.pl/wyklady/nai/scb/wyklad3/w3.htm
Masz tutaj chyba to co chciałeś, parametr alfa daje odchylenie

0

@xxx_xx_x: @MarekR22 Tez tak myślałem, że sigmoid, ale u niego output jest tam gdzie normalnie input!

0

Ogólnie najlepiej spisałyby się krzywe sklejane. Wiem bo pisałem dokładnie takie coś :D wtedy idealnie zachowa się tak jak @furious programming oczekuje. Tyle że jest to trudniejsze do zaimplementowania :d

0

No dobrze, dzięki za odpowiedzi — ten sigmoid wygląda obiecująco. Spróbuję się tym pobawić. ;)

1

@furious programming:

Masz gotowca z b-spline:

package pl.bspline

import java.awt.Graphics
import javax.swing.JFrame
import javax.swing.JPanel
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Dimension


data class Vector2(val x: Double, val y:Double) {
    operator fun times(a : Double): Vector2 {
        return Vector2(x * a, y * a)
    }

    operator fun plus(a : Vector2): Vector2 {
        return Vector2(x + a.x, y + a.y)
    }
}

class BSpline(alpha: Double) : JFrame() {

    init {
        add(createCanvas(alpha).also { it.preferredSize = Dimension(512, 512) })
        pack()
    }

    private fun createCanvas(alpha: Double): JPanel = object : JPanel() {

        val p = listOf<Vector2>(
            Vector2(0.0, 0.0),
            Vector2(0.5 + alpha, 0.5 - alpha),
            Vector2(0.5 - alpha, 0.5 + alpha),
            Vector2(1.0, 1.0)
        )

        override fun paintComponent(g: Graphics?) {
            super.paintComponent(g)
            g?.color = Color.BLACK
            g?.fillRect(0, 0, width, height)

            var step = 1.0/512.0

            var t = step
            var prev = p[0]
            var next = evalBezier(t, p[0], p[1], p[2], p[3])
            g?.color = Color.WHITE
            while(t <= 1.0) {
                g?.drawLine((width * prev.x).toInt(), height - (height * prev.y).toInt(), (width * next.x).toInt(), height - (height * next.y).toInt())
                t += step
                prev = next
                next = evalBezier(t, p[0], p[1], p[2], p[3])
            }

        }
    }

    private fun evalBezier(t: Double, p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2): Vector2 {
        val tt = t * t
        val ttt = t * t * t
        val u = 1 - t
        val uuu = u * u * u
        val q2 = 3f * ttt - 6f * tt + 3f * t
        val q1 = -3f * ttt + 3f * tt
        return p0 * uuu +
                p1 * q2 +
                p2 * q1 +
                p3 * ttt
    }


}

fun main() {
    BSpline(0.3).also {
        it.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        it.isVisible = true
    }
}
0

Dzięki @xxx_xx_x. ;)

Patrzę sobie jeszcze na tę funkcję sigmoidalną (czy tam aktywacji) i skłaniam się ku niej ze względu na prostotę obliczeń. Spełnia wymagania, ale przydałoby mi się jakieś narzędzie do wizualizacji wyników, w którym mógłbym ustawić min/max oraz parametr a (jako gain), coby sobie dobrać odpowiedni zakres krzywizn.

Znalazłem jedno takie narzędzie — https://keisan.casio.com/exec/system/15157249643325 — ale to nie pozwala na jakąkolwiek normalizację. Zna ktoś coś podobnego, ale bogatszego w ustawienia?

0

Z ta krzywa to tez jest łatwo, robisz sobie po prostu palete[256] zdaje sie wystarczy tak:

for(i in 0..255) {
  palete[i] = evalBezier(i/255.0, p[0], p[1], p[2], p[3]) * 255
}

Możesz też bez palety po prostu użyć eval i jako pierwszy parametr kolor/255.0

0

Tzn. z tego co zdążyłem wybadać, sigmoid jest w zupełności wystarczający, dla parametru a z przedziału od 0.1 (praktyczny brak zmiany wartości wejściowej) do 1.0 (totalna polaryzacja wejścia). Tak więc na tym zakończę poszukiwania — wszystkim dziękuję za odpowiedzi!

2

tanh, ale ogólnie to poszukałbym parametryzowanej sigmoidy bo pamiętam że można było sterować brzuszkami w pewnym zakresie.
https://www.researchgate.net/figure/Commonly-used-activation-functions-a-Sigmoid-b-Tanh-c-ReLU-and-d-LReLU_fig3_335845675

To jest chyba najlepsze:
43115860faf99cf3306b5000b365141a76dc758b.png

Źródło: https://pl.qaz.wiki/wiki/Logistic_function

0

y = x^k / 2; k > 1 -> wklęsłe, k < 1 wypukłe.

dla x > 1/2 -> odwracamy i przesuwamy, czyli:
y = 1 - y(1-x); chyba.

ewentualnie zwyczajny sin(x)^2 + modyfikacje...

1 użytkowników online, w tym zalogowanych: 0, gości: 1