programing

플로트 0에서 색상 값을 변환하는 중..1에서 바이트 0까지...255

css3 2023. 9. 27. 18:06

플로트 0에서 색상 값을 변환하는 중..1에서 바이트 0까지...255

색상 값을 플로트에서 바이트로 변환하는 올바른 방법은 무엇입니까?요.b=f*255.0 해야겠지만,만만1.0다로 됩니다.255,그렇지만0.9999e니다.254아마 내가 원하는 건 아닐 겁니다

인 것 같습니다.b=f*256.0 않는입니다를 는 더 2561.0.

결국 저는 이걸 사용합니다.

#define F2B(f) ((f) >= 1.0 ? 255 : (int)((f)*256.0))

1.0이 유일하게 오류가 발생할 수 있으므로 이 경우를 별도로 처리합니다.

b = floor(f >= 1.0 ? 255 : f * 256.0)

또한 반올림 오류로 인한 잘못된 동작(예: f=1.0000001)을 방지하기 위해 f가 실제로 0<=f<=1임을 강제할 가치가 있습니다.

f2 = max(0.0, min(1.0, f))
b = floor(f2 == 1.0 ? 255 : f2 * 256.0)

대체 안전 솔루션:

b = (f >= 1.0 ? 255 : (f <= 0.0 ? 0 : (int)floor(f * 256.0)))

아니면

b = max(0, min(255, (int)floor(f * 256.0)))

는 항상 를 해왔습니다.round(f * 255.0).

테스트(1의 특수한 경우) 및/또는 다른 답변에 클램핑이 필요 없습니다.이것이 여러분의 목적에 맞는 바람직한 답인지는 여러분의 목표가 입력 값과 가능한 가깝게 일치하는지, 아니면 각 구성요소를 256개의 등간격 [기타 공식]으로 나누는 것인지에 달려 있습니다.

제 수식의 단점은 0과 255 구간의 폭이 다른 구간의 절반에 불과하다는 것입니다.수년간 사용해 오면서, 저는 그것이 나쁘다는 시각적인 증거를 아직 보지 못했습니다.반대로, 저는 입력값이 상당히 가까울 때까지 어느 쪽이든 극단적으로 치지 않는 것이 바람직하다는 것을 알게 되었습니다 - 하지만 그것은 취향의 문제입니다.

가능한 장점은 R-G-B 성분의 상대적인 값이 더 넓은 범위의 입력 값에 대해 (약간) 더 정확하다는 것입니다.
각 를 얻기 (를 들어, 어떤 이 G 2 x 이 은 그 가 더 많을 이라고 믿습니다. 그 는 꽤 만인 다른 .에 대해 가장 가까운 정수를 얻기 위해 반올림한다는 것을 고려하면 직관적인 감각입니다. (예를 들어, 어떤 색이 G ~= 2 x R을 가지면 이 공식은 그 비율에 더 가까운 경우가 더 많을 것이라고 믿습니다.) 그 차이가 꽤 작고, 다른 많은 색들이 그 차이를 보일 수 있지만,256공식이 더 낫습니다.세탁물일 수도 있습니다.)

이나.256아니면255-기반 접근은 좋은 결과를 제공하는 것 같습니다.


255.256, 다른 방향을 조사하는 것입니다.
0에서 변환 중..255바이트에서 0.0..1.0 플로트.

0을 변환하는 공식..255개의 정수 값에서 0.0..1.0 범위의 등간격 값은 다음과 같습니다.

f = b / 255.0

를 .255아니면256: 위의 공식은 등간격 결과를 산출하는 공식입니다.다음을 사용하는지 관찰합니다.255.

의 하기 위해서 입니다.255두 방향의 공식들을 생각해보세요. 만약 당신이 2비트만 가지고 있다면, 따라서 정수 값 0..3:

을 이용한 그림3 2 유사합니다.2558비트 동안.변환은 위에서 아래로 또는 아래에서 위로 변환할 수 있습니다.

0 --|-- 1 --|-- 2 --|-- 3  
0 --|--1/3--|--2/3--|-- 1
   1/6     1/2     5/6

|는 4개의 범위 사이의 경계입니다.내부에서는 부동소수점 값과 정수 값이 범위의 중간 지점에 있음을 관찰합니다.두 표현 모두에서 모든 값 사이의 간격이 일정한지 확인합니다.

하면, 왜 한다면, 입니다를 할 수 것입니다.255- -256-기반 공식.


클레임: 사용하는 경우/ 255.0바이트에서 플로트로 이동할 때 사용하지 않습니다.round(f * 255.0)플로트에서 바이트로 이동할 때 "평균 왕복" 오류가 증가합니다.자세한 내용은 다음과 같습니다.

이는 플로트에서 시작하여 바이트로 이동한 다음 다시 플로트로 이동하여 가장 쉽게 측정할 수 있습니다.간단한 분석을 위해 2비트 "0..3인치 도표

~ 0 에서 1.0 합니다의 에서 이 .4가치.
반 간격길이 가 있습니다.에는 6다.
0..1/6, 1/6..1/3, .., 5/6..1
오차가 ,1/12(최소 오차는 0, 최대 오차는 1/6, 균등 분포)
합니다.1/12라운드 트립시의 전체 평균 오차입니다.

에 요.* 256아니면* 255.999공식, 대부분의 왕복 결과는 동일하지만 일부는 인접 범위로 이동됩니다.
다른 범위를 변경하면 오류가 증가합니다. 예를 들어 이전에 단일 플로트 입력의 오류가 1/6보다 약간 작다면 인접한 범위의 중심을 반환하면 1/6보다 약간 큰 오류가 발생합니다. 예를 들어 최적 공식에서 0.18 => 바이트 1 => 플로트 1/3 ~= 0.333, 오류 |0.33-0.18|=0.147; a를 이용하여2560=> formula=>트 0=>트 0,트0.18인 입니다에서 입니다.0.147.

사용하는 다이어그램* 4와 함께/ 3은 한 줄로 변환은 한 줄에서 다음 줄로 변환됩니다.
번째 하지 않음을 하십시오: 0.3/8, 3/8.5/8, 5/8.1.그 는 3/8, 2/8, 3/8다입니다.마지막 줄의 간격 경계가 첫 번째 줄과 다릅니다.

   0------|--3/8--|--5/8--|------1
         1/4     1/2     3/4
=> 0------|-- 1 --|-- 2 --|------3  

=> 0----|---1/3---|---2/3---|----1
       1/6       1/2       5/6

이러한 증가된 오류를 피할 수 있는 유일한 방법은 바이트에서 플로팅으로 이동할 때 몇 가지 다른 공식을 사용하는 것입니다.이 그 중 .256공식을 사용하면 최적의 역 공식을 결정할 수 있습니다.
바이트 값 단위로 해당 바이트 값이 된 부동 소수점 값의 중간점을 반환해야 합니다. ~ 3 ~ 0대 0, 3대 1하고요를 제외합니다.아니면 0에서 1/8, 3에서 7/8!위의 도표에서, 당신은 다시 중앙선에서 맨 위로 돌아가야만 합니다.)

하지만 이제 등간격 바이트 값을 취하여 등간격 플로트 값으로 변환하는 방어하기 어려운 상황이 발생할 것입니다.

에는 이 할 수 있습니다.255 0 의. 평균 간격 255: 평균 왕복 오차의 증가 또는 플로트 도메인에서 간격이 일정하지 않은 값.

이런 걸 해보는 건 어때요?

b=f*255.999

합니다를 합니다.f==1하지만 0.999는 여전히 255입니다.

정확하게 동일한 크기의 청크를 갖고 싶다면 다음이 가장 좋은 해결책입니다.그것은 다음의 범위를 변환합니다.[0,1].[0,256[.

#include <cstdint>
#include <limits>

// Greatest double predecessor of 256:
constexpr double MAXCOLOR = 256.0 - std::numeric_limits<double>::epsilon() * 128;

inline uint32_t float_to_int_color(const double color){
  return static_cast<uint32_t>(color * MAXCOLOR);
}

EDIT: 엡실론(1.0)*128을 사용하고 엡실론(1.0)*256.0을 사용하지 않는 이유를 명확히 설명합니다.cpp 표준은 기계 엡실론을 다음과 같이 지정합니다.

부동 소수점 유형 T로 표현되는 1.0과 다음 값 사이의 차이.

256.0을 지수 8과 가수 1.0으로 나타내므로 엡실론(256.0)이 너무 커서 지수가 7인 이전 수를 검색할 수 있습니다. 예:

   0 10000000111 0000000000000000000000000000000000000000000000000000 256.0
 - 0 11110100110 0000000000000000000000000000000000000000000000000000 eps(256.0)
_____________________________________________________________________
 = 0 10000000110 1111111111111111111111111111111111111111111111111110

다음 중 어느 것을 사용해야 합니까?

_____________________________________________________________________
 = 0 10000000110 1111111111111111111111111111111111111111111111111111

색상 값을 플로트에서 바이트로 변환하는 올바른 방법이 무엇을 의미합니까?과 같은 가 있습니까?[0,1[것들 하게 분포할 입니다.256로부터의 쓰레기통.0.255?

을 좀 더 위해 는 float에 를 가지고 .int요,어 a 같은 정수로 .uint_2 -현.r이unit2_t다 을 가질 수 .00b,01b,10b그리고.11b (b다.이를 Intel 규약)이라고도 합니다.그러면 어떤 실수 구간을 어떤 정수 값에 매핑해야 하는지 생각해 내야 합니다.요를 과 같이 하십시오.[0,0.25[.0,[0.25,0.5[.1,[0.5,0.75[.2그리고.[0.75,1.0].3은 , 에 수 .b = std::floor(f * 4.0)(floor는 정수 부분만 취하고 분수 부분은 무시합니다.)이것은 다음을 제외한 모든 숫자에 대해 작동합니다.f=1 사항은 . :b = floor(f >= 1.0 ? 255 : f * 256.0)이 문제를 해결할 수 있습니다.이 방정식을 사용하면 간격이 동일한 간격을 유지할 수 있습니다.

소수점 IEEE 754 한다면, 가능한 이 존재합니다.[0,1] 어떤 실수의 표현이 어떤 정수의 표현에 속하는지 결정해야 합니다.그런 다음 플로트 번호를 정수로 변환하는 소스 코드를 생각해 내고 매핑에 맞는지 확인할 수 있습니다.아마도요.int ig = int(255.99 * g);..b = floor(f >= 1.0 ? 255 : f * 256.0) 실수 에 매핑할지에 이것은 어떤 실수 표현을 어떤 정수 표현에 매핑할지에 따라 달라집니다.

다음 프로그램을 살펴봅니다.다른 변환이 다른 작업을 수행한다는 것을 보여줍니다.

#include <iostream>

constexpr int realToIntegerPeterShirley(const double value) {
    return int(255.99 * value);
}

#define F2B(f) ((f) >= 1.0 ? 255 : (int)((f)*256.0))
constexpr int realToIntegerInkredibl(const double value) {
    return F2B(value);
}

const int realToIntegerMarkByers(const double value) {
    return std::floor(value >= 1.0 ? 255 : value * 256.0);
}

constexpr int realToIntegerToolmakerSteve(const double value) {
    return std::round(value * 255.0);
}

constexpr int realToIntegerErichKitzmueller(const double value) {
    return value*255.999;
}

constexpr int realToInteger(const float value) {
    return realToIntegerInkredibl(value);
}

int main() {
    {
        double value = 0.906285;
        std::cout << realToIntegerMarkByers(value) << std::endl; // output '232'
        std::cout << realToIntegerPeterShirley(value) << std::endl; // output '231'
    }

    {
        double value = 0.18345;
        std::cout << realToIntegerInkredibl(value) << std::endl; // output '46'
        std::cout << realToIntegerToolmakerSteve(value) << std::endl; // output '47'
    }

    {
        double value = 0.761719;
        std::cout << realToIntegerVertexwahn(value) << std::endl; // output '195'
        std::cout << realToIntegerErichKitzmueller(value) << std::endl; // output '194'
    }
}

이 작은 테스트 베드를 사용하여 실험을 수행할 수 있습니다.

int main() {
    std::mt19937_64 rng;
    // initialize the random number generator with time-dependent seed
    uint64_t timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
    std::seed_seq ss{uint32_t(timeSeed & 0xffffffff), uint32_t(timeSeed>>32)};
    rng.seed(ss);
    // initialize a uniform distribution between 0 and 1
    std::uniform_real_distribution<double> unif(0, 1);
    // ready to generate random numbers
    const int nSimulations = 1000000000;
    for (int i = 0; i < nSimulations; i++)
    {
        double currentRandomNumber = unif(rng);

        int firstProposal = realToIntegerMarkByers(currentRandomNumber);
        int secondProposal = realToIntegerErichKitzmueller(currentRandomNumber);

        if(firstProposal != secondProposal) {
            std::cout << "Different conversion with real " << currentRandomNumber << std::endl;
            return -1;
        }
    }
}

마지막으로 저는 플로트에서 정수로 변환하지 말 것을 제안합니다.이미지를 하이 다이내믹 레인지 데이터로 저장하고 데이터를 로우 다이내믹 레인지로 변환하는 도구(예: http://djv.sourceforge.net/) )를 선택합니다.톤 매핑은 자체 연구 영역이며 좋은 사용자 인터페이스를 가진 몇몇 도구들은 여러분에게 모든 종류의 톤 맵 연산자를 제공합니다.

float을 정수로 비교할 때 허용된 솔루션이 실패했습니다.

이 코드는 정상적으로 작동합니다.

float f;
uint8_t i;
//byte to float
f =CLAMP(((float)((i &0x0000ff))) /255.0, 0.0, 1.0);
//float to byte
i =((uint8_t)(255.0f *CLAMP(f, 0.0, 1.0)));

CLAMP가 없는 경우:

#define CLAMP(value, min, max) (((value) >(max)) ? (max) : (((value) <(min)) ? (min) : (value)))

또는 전체 RGB의 경우:

integer_color =((uint8_t)(255.0f *CLAMP(float_color.r, 0.0, 1.0)) <<16) |
               ((uint8_t)(255.0f *CLAMP(float_color.g, 0.0, 1.0)) <<8) |
               ((uint8_t)(255.0f *CLAMP(float_color.b, 0.0, 1.0))) & 0xffffff;

float_color.r =CLAMP(((float)((integer_color &0xff0000) >>16)) /255.0, 0.0, 1.0);
float_color.g =CLAMP(((float)((integer_color &0x00ff00) >>8)) /255.0, 0.0, 1.0);
float_color.b =CLAMP(((float)((integer_color &0x0000ff))) /255.0, 0.0, 1.0);

각 방법의 장단점:

  1. (f * 256).clip(0, 255)
    • ✓ 균일한 크기의 간격.
    • ✗ 작은 노이즈 항을 추가해도 원래 이미지가 올바르게 복구되지 않습니다.
  2. (f * 255.999)
    • ✓ 균일한 크기의 간격(오차 0.0004% 미만).
    • ✗ 작은 노이즈 항을 추가해도 원래 이미지가 올바르게 복구되지 않습니다.
    • ✓ 가장 빨리.
  3. (f * 255).round()
    • ✗ 구간의 크기를 [1, 254] 범위 내에서 균일하게 조정하지만 끝점 0과 255의 경우 구간 크기의 절반을 사용합니다.
    • ✓ 원래 이미지를 올바르게 복구합니다.

권장 사항:

  • 방법 1을 사용할 경우f는 "random 변수"이거나 처리되지 않은 이미지에서 가져온 것이 아닙니다.
  • 빠르고 간단한 것을 원한다면 방법 2를 사용하세요.
  • 3이 합니다하는 원래 합니다.f에서 생성됩니다.

테스트

>>> x = np.arange(256)
[0, 1, 2, ..., 253, 254, 255]

>>> f = x / 255
[0.000, 0.004, 0.008, ..., 0.992, 0.996, 1.000]

>>> def test(func, eps=1e-3):
...     print(
...         (x == func(f - eps)).all(),
...         (x == func(f)).all(),
...         (x == func(f + eps)).all(),
...     )

합니다를 가장 잘 합니다.x값에서 입니다.f:

>>> test(lambda f: (f * 256).clip(0, 255).astype(np.uint8))
False True False

>>> test(lambda f: (f * 255.999).astype(np.uint8))
False True False

>>> test(lambda f: (f * 255).round().astype(np.uint8))
True True True

원형이 아닌 바닥(f*256)이 맞는 것 같습니다.간격 0을 매핑합니다.길이가 같은 1 ~ 정확히 256개의 구역입니다.

[EDIT] 및 256을 특수한 경우로 확인합니다.

public static void floatToByte(float f)
{
     return (byte)(f * 255 % 256)
}

값 < 1은 정확하게 변환됩니다.

변환 후 255에서 256 사이에 속하는 값은 바이트로 변환 시 255로 바닥됩니다.

> 1 하여 > 1 으로 0 을 .%교환입니다.

clamp(round(f * 256 - 0.5), 0, 255)

min(max(round(f * 256 - 0.5), 0), 255)

clamp(floor(f * 256), 0, 255)에서 반올림)

min(max(floor(f * 256), 0), 255)에서 반올림)

위의 공식들은 플로트 [0..1]에서 플로트 [-0.5]로 변환됩니다.255.5]에서 바이트 [0.255]로 바꿉니다.하지만 Direct3D 데이터 변환 규칙을 확인해 보면 그들은 다르게 합니다.다음과 동등한 것:

floor(clamp(f, 0, 1) * 255 + 0.5)에서 반올림)

floor(min(max(f, 0), 1) * 255 + 0.5)에서 반올림)

이 공식은 플로트 [-0.00196..1.00196]에서 플로트 [-0.5]로 변환됩니다.255.5]에서 바이트 [0.255]로 바꿉니다.

유사한 접근 방식은 다음과 같습니다.

round(clamp(f, 0, 1) * 255)

round(min(max(f, 0), 1) * 255)

언급URL : https://stackoverflow.com/questions/1914115/converting-color-value-from-float-0-1-to-byte-0-255