github.com/kaydxh/golang@v0.0.131/pkg/gocv/cvtable/cvtable.go (about)

     1  /*
     2   *Copyright (c) 2023, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package cvtable
    23  
    24  import (
    25  	"fmt"
    26  	"sort"
    27  	"strconv"
    28  
    29  	io_ "github.com/kaydxh/golang/go/io"
    30  	exp_ "github.com/kaydxh/golang/go/math/exp"
    31  )
    32  
    33  const (
    34  	validateRows = 101
    35  	maxScore     = 100
    36  	minScore     = 0
    37  )
    38  
    39  type CVTable struct {
    40  	table []float64
    41  }
    42  
    43  func NewCVTable(filepath string) (*CVTable, error) {
    44  	table, err := io_.ReadFileLines(filepath)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	c := &CVTable{}
    50  	if table[0] != "0" {
    51  		c.table = append(c.table, 0)
    52  	}
    53  
    54  	for _, v := range table {
    55  		sim, err := strconv.ParseFloat(v, 64)
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  		c.table = append(c.table, sim)
    60  	}
    61  	err = c.Validate()
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	return c, nil
    66  }
    67  
    68  func (c CVTable) Validate() error {
    69  	if len(c.table) != validateRows {
    70  		return fmt.Errorf("invalid cv table rows %v, must be %v", len(c.table), validateRows)
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  // score 0-100, mapping to sim
    77  func (c CVTable) Sim(score float64) float64 {
    78  	if score <= minScore {
    79  		return 0
    80  	}
    81  
    82  	// integerPart integer part of a decimal
    83  	// decimalPart  decimal part of a decimal
    84  	integerPart := int(score)
    85  	decimalPart := score - float64(integerPart)
    86  
    87  	// len(c.table) - 1 == 100
    88  	if integerPart >= len(c.table)-1 {
    89  		return 1
    90  	}
    91  
    92  	return c.table[integerPart] + (c.table[integerPart+1]-c.table[integerPart])*decimalPart
    93  }
    94  
    95  func (c CVTable) Score(sim float64) float64 {
    96  	if sim <= 0 {
    97  		return minScore
    98  	}
    99  	if sim >= 1 {
   100  		return maxScore
   101  	}
   102  
   103  	pos := sort.Search(len(c.table), func(i int) bool { return c.table[i] >= sim })
   104  	score := float64(pos)
   105  	if pos > 0 && pos < len(c.table) {
   106  		score += -1.0 + (sim-c.table[pos-1])/(c.table[pos]-c.table[pos-1])
   107  	}
   108  
   109  	return exp_.Value(score, minScore, maxScore)
   110  }