Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
14 Views

ippiScaleC_8u_C1IR with negative mVal bug ?

Hi,
I would like to achieve image inverse. So [src] = 255-[src]

I use: ippiScaleC_8u_C1IR(pSrc, Width, -1, 255, roiSize, ippAlgHintFast) , but it does not work.
Source image does not changed ...

 

57  
0 Kudos
21 Replies
Highlighted
Employee
12 Views

Hi Ladislav,

it's better (at least significantly faster) to use the next API (in a loop - row by row)

/* ////////////////////////////////////////////////////////////////////////////
//  Names:       ippsSubCRev
//  Purpose:     Subtracts each element of vector from constant value
//  Arguments:
//    pSrc             Pointer to source vector
//    pSrcDst          Pointer to source and destination vector for in-place operation
//    pDst             Pointer to destination vector
//    val              Scalar value from which vector elements are subtracted
//    len              Number of elements in vector
//    scaleFactor      Scale factor
//  Return:
//    ippStsNullPtrErr At least one of the pointers is NULL
//    ippStsSizeErr    Vectors' length is less than 1
//    ippStsNoErr      No error
//  Note:
//    SubCRev(X,v,Y) :  Y = v - X
*/
IPPAPI(IppStatus, ippsSubCRev_8u_ISfs,   (                     Ipp8u   val, Ipp8u*   pSrcDst, int len, int scaleFactor))
IPPAPI(IppStatus, ippsSubCRev_8u_Sfs,    (const Ipp8u*   pSrc, Ipp8u   val, Ipp8u*   pDst,    int len, int scaleFactor))
 

as regarding ScaleC - please provide compilable reproducer.

regards, Igor

0 Kudos
Highlighted
Beginner
12 Views

Hi Igor,
thank you. Your suggestion works for me!

Regarding ScaleC I can provide only pseudo code in C (as I am developing using Free Pascal):
(source is taken from Scale.c example)

    int main(void)
    {
        IppStatus status = ippStsNoErr;

        IppiSize roiSize = {8, 8}; /* Image sizes */
        Ipp8u pSrc[8*8]={0};       /* Pointers to source images */
        Ipp64f mVal, aVal;

        mVal = -1;
        aVal = 255;

        check_sts( status = ippiScaleC_8u_C1IR(pSrc, roiSize.width, mVal, aVal, roiSize, ippAlgHintFast) )

    EXIT_MAIN
        printf("Exit status %d (%s)\n", (int)status, ippGetStatusString(status));
        return (int)status;
    }

Expected result is 255 in all elements of pSrc

 

0 Kudos
Highlighted
Employee
12 Views

I haven't faced with any issues:

#include "ipp.h"
#include <stdio.h>
 
int main(void)
    {
        IppStatus status = ippStsNoErr;
        IppiSize roiSize = {8, 8}; /* Image sizes */
        Ipp8u pSrc[8*8]={0};       /* Pointers to source images */
        Ipp64f mVal, aVal;
        mVal = -1;
        aVal = 255;
 
        printf("Before: ");
        for( int i = 0; i < 8*8; i++ ) printf( "%d,", pSrc );
        printf("\n");
        status = ippiScaleC_8u_C1IR(pSrc, roiSize.width, mVal, aVal, roiSize, ippAlgHintFast);
        printf("After: ");
        for( int i = 0; i < 8*8; i++ ) printf( "%d,", pSrc );
        printf("\n");
        printf("Exit status %d (%s)\n", (int)status, ippGetStatusString(status));
        return (int)status;
}
Before: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
After: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Exit status 0 (ippStsNoErr: No errors.)
Press any key to continue . . .
Regards, Igor
0 Kudos
Highlighted
Beginner
12 Views

I link dynamically to DLLs supplied with IPP 2017.2.178 (ippGetLibVersion = ippCore2017.0.2 (r54644))
Interesting is that for "s8" versions it works as expected, but for "p8" and "h9" versions given wrong results.

Try this example please:

int main(void)
    {
        IppStatus status = ippStsNoErr;
        IppiSize roiSize = {8, 8};  /* Image sizes */
        Ipp8u pSrc[8*8]={100};       
        Ipp64f mVal, aVal;
        mVal = 1;
        aVal = 10;

 

        printf("Before: ");
        for( int i = 0; i < roiSize.Width*roiSize.Height; i++ ) printf( "%d,", pSrc );
        printf("\n");
        status = ippiScaleC_8u_C1IR(pSrc, roiSize.width, mVal, aVal, roiSize, ippAlgHintFast);
        printf("After: ");
        for( int i = 0; i < roiSize.Width*roiSize.Height; i++ ) printf( "%d,", pSrc );
        printf("\n");
        printf("Exit status %d (%s)\n", (int)status, ippGetStatusString(status));
        return (int)status;
}

All values of 8x8 array are initialised to 100, then ScaleC should add 10 to each element.
I get for first 16 values result 110 which is Ok, but next values are 120, which is NOT ok (seems like some bytes are processed twice!)

0 Kudos
Highlighted
Employee
12 Views

Hi Ladislav,

you example is not compilable - compiler doesn't know Width and Height... I extended my example with dispatching for all supported ia32 architectures - see result below:

#include "ipp.h"
#include <stdio.h>
 
static void libInfo( void ){
    const IppLibraryVersion *lib;
 
    lib = ippiGetLibVersion();
    printf( "CPU       : %s\n", lib->targetCpu );
    printf( "Name      : %s\n", lib->Name );
    printf( "Version   : %s\n", lib->Version );
}
 
static void ScaleC(void)
    {
        IppStatus status = ippStsNoErr;
        IppiSize roiSize = {8, 8};  /* Image sizes */
        Ipp8u pSrc[8*8];      
        Ipp64f mVal, aVal;
        mVal = 1;
        aVal = 10;
        int i;
 
        for( i = 0; i < roiSize.width*roiSize.height; i++ ) pSrc = 100;
        printf("Before: ");
        for( i = 0; i < roiSize.width*roiSize.height; i++ ) printf( "%d,", pSrc );
        printf("\n");
        status = ippiScaleC_8u_C1IR(pSrc, roiSize.width, mVal, aVal, roiSize, ippAlgHintFast);
        printf("After: ");
        for( i = 0; i < roiSize.width*roiSize.height; i++ ) printf( "%d,", pSrc );
        printf("\n");
        printf("Exit status %d (%s)\n", (int)status, ippGetStatusString(status));
}
 
int main( void ){
    Ipp64u features[] = { ippCPUID_SSE, ippCPUID_SSE2, ippCPUID_SSSE3, ippCPUID_SSE42, ippCPUID_AVX, ippCPUID_AVX2 };
    int i;
 
    for( i = 0; i < sizeof(features)/sizeof(features[0]); i++ ){
        ippSetCpuFeatures( features );
        libInfo();
        ScaleC();
    }
    return 0;
}

CPU       : px
Name      : ippIP PX (px)
Version   : 2017.0.2 (r54644)
Before: 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
After: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
Exit status 0 (ippStsNoErr: No errors.)
CPU       : w7
Name      : ippIP SSE2 (w7)
Version   : 2017.0.2 (r54644)
Before: 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
After: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
Exit status 0 (ippStsNoErr: No errors.)
CPU       : s8
Name      : ippIP Atom (s8)
Version   : 2017.0.2 (r54644)
Before: 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
After: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
Exit status 0 (ippStsNoErr: No errors.)
CPU       : p8
Name      : ippIP SSE4.2 (p8)
Version   : 2017.0.2 (r54644)
Before: 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
After: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
Exit status 0 (ippStsNoErr: No errors.)
CPU       : g9
Name      : ippIP AVX (g9)
Version   : 2017.0.2 (r54644)
Before: 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
After: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
Exit status 0 (ippStsNoErr: No errors.)
CPU       : h9
Name      : ippIP AVX2 (h9)
Version   : 2017.0.2 (r54644)
Before: 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
After: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,
Exit status 0 (ippStsNoErr: No errors.)
Press any key to continue . . .

regards, Igor.

 

PS regarding Threshold - if you use

                  ippiThreshold_LTVal_8u_C1IR and  ippiThreshold_GTVal_8u_C1IR

​in a row by row loop with roi.height=1 - (or several rows at once with condition roi.height*roi.width < 32K (L0 size)) - you'll get the same performance as if it is ippiThreshold_LTValGTVal_8u_C1IR()

0 Kudos
Highlighted
Beginner
12 Views

Thank you Igor,

can you please download and test my test program: http://uschovna.zoznam.sk/download?code=1342688553904-hUpDpkVrWZRcJfSiiYkL
(there are ZIPed also DLLs which I use)
What result do you get ?

I get in 1st case: all 110,... as expected
But in 2nd case: 16x 110 followed by 120: 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,120,...

This is Pascal source code compiled:

const
  CpuFeatures: array[0..1] of Ipp64u = (
    ippCPUID_MMX+ippCPUID_SSE+ippCPUID_SSE2+ippCPUID_SSE3+ippCPUID_SSSE3,
    ippCPUID_MMX+ippCPUID_SSE+ippCPUID_SSE2+ippCPUID_SSE3+ippCPUID_SSSE3+ippCPUID_SSE41+ippCPUID_SSE42+ippCPUID_AVX+ippCPUID_AVX2);

var
  lv: PIppLibraryVersion;
  b: array[0..8*8] of byte;
  j,i: integer;
  roiSize: ippiSize;

begin
  writeln('ippInit=',ippInit);
  for j:=low(CpuFeatures) to high(CpuFeatures) do begin
    writeln;
    writeln('ippSetCpuFeatures=', ippSetCpuFeatures(CpuFeatures));
    lv := ippGetLibVersion;
    writeln('ippGetLibVersion=', lv^.Name, lv^.Version, lv^.targetCpu);

    roiSize.width:=8;
    roiSize.height:=8;
    writeln('ROI=',roiSize.width,'x',roiSize.Height);
    FillByte(b[0], Length(b), 100);
    write('Before:');
    for i:=0 to 40 do write(b:3,',');
    writeln;
    writeln('ippiScaleC_8u=', ippiScaleC_8u_C1IR(@b[0], roiSize.Width, 1, 10, roiSize, ippAlgHintFast));
    write('After :');
    for i:=0 to 40 do write(b:3,',');
    writeln;
  end;
  readln;
end.

 

PS:

regarding ippiThreshold_LTValGTVal_8u_C1IR() why is there strange limitiation, that thresholdLT must be <= thresholdGT?
(without this limitation it will be usable for binarization and efficient at least as as are two calls to Threshold_LTVal and Threshold_GTVal)

0 Kudos
Highlighted
Beginner
12 Views

Can anyone please download my example program from above mentioned link and check/report results ?

0 Kudos
Highlighted
Employee
12 Views

Hi Ladislav,

I don't have Pascal installed. Could you provide an output in the same manner as I've provided - Src content before ScaleC and then after + library version - in order to check if we are playing with the same IPP build and src/dst content is as you've described.

Regards, Igor

0 Kudos
Highlighted
New Contributor I
12 Views

I would like to achieve image inverse. So [src] = 255-[src]

I wonder if that couldn't be done with ippsSubCRev_8u_ISfs, but I didn't try it out myself.

Regards,

Adriaan van Os

0 Kudos
Highlighted
Beginner
12 Views

@Igor: on this link https://moja.uschovna.zoznam.sk/link/586b2b9b-23e2-4f8c-91a6-57de4bd84444  is already compiled program + DLLs which I use.
Output is:

ippInit=ippStsNoErr

ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP Atom (s8)2017.0.2 (r54644)s8
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After /*here is all as expected */:
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,

ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP AVX2 (h9)2017.0.2 (r54644)h9
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After /*here is only first 16 bytes as expected, next are wrong ... seems like they are processed twice*/ :
110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,

@Adrian: Yes  ippsSubCRev_8u_ISfs works and I use it for now (as workaround). But I wonder why does not work for me ippiScaleC_8u

0 Kudos
Highlighted
Beginner
12 Views

Can somebody test using dynamically loaded distributed DLLs. My results:

For Win32 "w7" and "s8" gives correct results, while others not (with same program code)

Win32:
------
ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP SSE2 (w7)2017.0.2 (r54644)w7
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After :110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,

ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP Atom (s8)2017.0.2 (r54644)s8
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After :110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,

ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP SSE4.2 (p8)2017.0.2 (r54644)p8
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After :110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,

ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP AVX2 (h9)2017.0.2 (r54644)h9
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After :110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,

Win64:
------
ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP SSE4.2 (y8)2017.0.2 (r54644)y8
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After :110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,

ippSetCpuFeatures=ippStsNoErr
ippiGetLibVersion=ippIP AVX2 (l9)2017.0.2 (r54644)l9
ROI=8x8
Before:100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
ippiScaleC_8u=ippStsNoErr
After :110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,

0 Kudos
Highlighted
Employee
12 Views

Hi Ladislav,

your link shows "Stránka sa nenašla" (404)

regards, Igor

0 Kudos
Highlighted
Beginner
12 Views

Hi Igor,
uploaded file probably expired. I have uploaded it again: https://moja.uschovna.zoznam.sk/link/2fda80c2-7e14-4c80-9b7c-d48e9df0a0a5
Please check ...

0 Kudos
Highlighted
Employee
12 Views

Hi Ladislav,

got it "Zadarma" :)

Please don't expect the quick diagnose - I've reproduced your issue with your executable - even when replaced dlls with the latest ones from trunk. You see that there is no such issue if I use "my" test - therefore I need to build debug version of the library in order to understand the root of the issue.

regards, Igor

0 Kudos
Highlighted
Employee
12 Views

Hi Ladislav,

thank you that you've insisted on this bug investigation. This is real bug in IPP code that will be fixed in the next IPP version. It can't be reproduced "by default" with MSVC and my C-test and only your Pascal example has caught it with so simple test-case. There are two problems in the function code: (1) wrong test for "invalid" flag in mxcsr register and (2) wrong approach for in-place flavor of this function if "invalid" is raised up. For performance purpose there is no test for integer overflow in the main loop - I guess it's clear that in 99.999999% of cases there will be no overflow: the optimized (SSE,AVX) version loads 8u data, converts it to 32s, then to 32f (if "fast" hint is set), then scales it with mpy and add factors. If these factors are huge - the conversion back to 32s may overflow and raise "invalid" mxcsr flag. In this case, after the main loop, this flag is analyzed, and if it is set - the function goes to the second loop, that performs source vector processing from the beginning with correct saturation. This approach works correctly for out-of-place case, but fails for in-place (your case, bug #1), The difference between Pascal and MSVC is that MSVC masks all FP exceptions, while Pascal - doesn't. Because of the bug #2 - wrong analysis of "invalid" flag and mask combination - if "invalid" mask == 0 (your case - default settings for Pascal) - the code of the main loop of ScaleC function always detects "overflow" case and starts the second "saturation" loop. The main loop works with aligned on 0x20 data 32 elements at once, your vector is aligned on 0x10 - therefore the first 16 elements are calculated correctly in prolog code that always checks overflow. The same is true for epilog.

regards, Igor

0 Kudos
Highlighted
Beginner
12 Views

Hi Igor,
thank you very much for your investigation. I will wait for next release and retest. Do you think, that also some other IPP function can be affected by different masking of FP exceptions in Pascal ?

0 Kudos
Highlighted
New Contributor I
12 Views

Note that you can call 

SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision])

 

to get the same (buggy) behaviour as in C. The macpas.pp unit (in the FreePascal runtime) even does this automatically, because of the many bugs in Mac OS X. See http://bugs.freepascal.org/view.php?id=11516.

Regards,

Adriaan van Os

0 Kudos
Highlighted
New Contributor I
12 Views

@Igor

I don't know if you have an internal test-suite for IPP. If you have, I suggest to run it (or run it too) with unmasked floating-point exceptions (by unmasking them manually at the start of the test-suite). Ot maybe add such a masking/unmasking call to IPP.

Regards,

Adriaan van Os

0 Kudos
Highlighted
Beginner
12 Views

@Adriaan: Thank you. I can confirm, that when I add Math unit and SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]) then my test program gives correct results.

0 Kudos