How Cloudy Is It?

22

5

Challenge

Given an image of the sky, you must output the cloud cover in oktas. The image supplied will be an image file (the type is up to you) and the output should be to STDOUT.

Oktas

In meteorology, an okta is a unit of measurement used to describe the amount of cloud cover at any given location such as a weather station. Sky conditions are estimated in terms of how many eighths of the sky are covered in cloud, ranging from 0 oktas (completely clear sky) through to 8 oktas (completely overcast).

The sky will always be a picture from around midday (so, blue sky, not red/night sky).

The colour of a cloud will always be a colour which follows the following pattern:

#ABCDEF

Where AB >= C0, CD >= C0 and EF >= C0.

Or, in RGB:

(A, B, C)

Where A >= 192, B >= 192 and C >= 192.

Here are the percentage coverages related to the oktas:

0%    - 0 oktas
12.5% - 1 okta
25%   - 2 oktas
37.5% - 3 oktas
50%   - 4 oktas
62.5% - 5 oktas
75%   - 6 oktas
87.5% - 7 oktas
100%  - 8 oktas

The percentages are the percentage of the image which is cloud.

If you the percentage cloud of your image is not a multiple of 12.5, you should round to the nearest.

Output

The output should just be the okta number (you don't have to say the unit).

Examples

1 okta (18.030743615677714% cloud)

0 oktas (0.0% cloud)

3 oktas (42.66319444444445% cloud)

1 okta (12.000401814778645% cloud)

Python code used to calculate numbers

Winning

Shortest code in bytes wins.

Beta Decay

Posted 2017-07-28T13:01:30.040

Reputation: 21 478

Isn't the last one 3 oktas? – TheLethalCoder – 2017-07-28T13:16:17.170

@TheLethalCoder Whoops, edited – Beta Decay – 2017-07-28T13:34:36.680

Is there a maximum to the dimensions of an image? – Shaggy – 2017-07-28T14:16:29.997

@Shaggy Not really. As large as your language can handle – Beta Decay – 2017-07-28T14:17:22.527

2I've added a 4th test case that requires rounding up to 12.5, since answers using integer flooring would've been passing the first 3 test cases. – Justin Mariner – 2017-07-28T17:59:18.933

@JustinMariner Good call :D – Beta Decay – 2017-07-28T18:32:13.723

1About languages that don't have image processing capabilities like C++, is it ok to use a library ? If so, for the byte count, should it count only the written code or also the size of DLL files required to run the program ? – HatsuPointerKun – 2017-07-28T20:32:34.863

@HatsuPointerKun Yeah, that's allowed like how Pythoj uses PIL – Beta Decay – 2017-07-28T20:34:24.317

@Hatsu It's only the byte count of the driver program that counts. So, in C++, you would need at least one #include directive, and that would be included in your byte count. But not the byte count of the library code. (I'm not sure about build flags that control linking to the library. I could argue either way, and I can't find a community consensus about this. Low-level languages don't get a lot of love around here, in my opinion. :-() – Cody Gray – 2017-07-29T11:45:55.190

Answers

10

Python 2, 114 110 98 bytes

-4 bytes thanks to TheLethalCoder
-12 bytes thanks to Ruud

import PIL.Image as P
i=P.open(input()).getdata()
print round(8.*sum(min(x)>191for x in i)/len(i))

Try it online!

Rod

Posted 2017-07-28T13:01:30.040

Reputation: 17 588

Use 191 instead? – TheLethalCoder – 2017-07-28T13:46:32.170

2I was going to suggest x&y&z&192>191, but the updated version is just as short. – Arnauld – 2017-07-28T13:57:05.760

98 bytes – Arfie – 2017-07-28T13:58:14.487

2Could you potentially replace import PIL.Image as P with from PIL.Image import* and save 1 byte when changing i=P.open to i=open? I don't know if that'd cause problems since open is already a defined function, but I can't test since I don't have the ability to install the module. – Arnold Palmer – 2017-07-28T14:21:01.830

1Yeah, that seems to work. Saves 1 byte. – Arfie – 2017-07-28T14:24:31.950

@ArnoldPalmer PIL won't allow import* on some plataforms – Rod – 2017-07-28T14:27:04.867

2@Rod your code doesn’t have to run on all platforms - the language is defined by the interpreter. If it runs for you then it’s valid. – Tim – 2017-07-29T11:35:23.073

10

MATL, 18 17 bytes

Yi191>3&A1eYm8*Yo

Example runs with the four provided images (sorry about preview quality; click for full resolution):

enter image description here

Or remove the last four characters to see the results without rounding:

enter image description here

Explanation

Yi     % Implicitly input filename or URL. Read image. Gives an M×N×3 array
191>   % Does each entry exceed 191?
3&A    % True for 3rd-dim "lines" that only contain true. Gives an M×N matrix
1e     % Linearize (flatten) into a 1×L row vector, with L = M*N
Ym     % Mean of vector
8*     % Multiply by 8
Yo     % Round. Implicitly display

Luis Mendo

Posted 2017-07-28T13:01:30.040

Reputation: 87 464

I wonder what can be done using esolangs – Евгений Новиков – 2017-07-31T14:47:49.393

6

Java (OpenJDK 8), 204 bytes

i->{int x=0,y=0,t=0,w=i.getWidth(),h=i.getHeight();for(;x<w;)for(y=0;y<h;){java.awt.Color c=new java.awt.Color(i.getRGB(x++,y++));if(c.getRed()>191&&c.getBlue()>191&&c.getGreen()>191)t++;}return 8*t/w/h;}

Try it online! I always forget that TIO outputs STDERR to the debug tab.. Maybe it could like highlight red in case of an error?

Roman Gräf

Posted 2017-07-28T13:01:30.040

Reputation: 2 915

Few things: your code currently runs in an infinite loop, since you never increment x or y. You assigned y=0 twice, so you can remove the first assignment. The class Color must be either fully-qualified (java.awt.Color) or you must include the import in your byte count. And your code fails for the 4th test case (returns 0 instead of 1). – Justin Mariner – 2017-07-28T19:16:02.317

I know it's been a while, but you can golf 6 bytes by removing the brackets of the inner for-loop, and changing the && to & and ,y=0 to ,y: Try it online.

– Kevin Cruijssen – 2018-05-03T07:19:11.160

6

C#, 150 146 bytes

b=>{int t=0,c=0,w=0,h;for(;w<b.Width;++w)for(h=0;h<b.Height;++t){var p=b.GetPixel(w,h++);if(p.R>191&p.G>191&p.B>191)c++;}return(int)(c/(t+0d)*8);}

Saved 4 bytes thanks to @Ian H.

Full/Formatted version:

using System.Drawing;

namespace System
{
    class P
    {
        static void Main()
        {
            Func<Bitmap, int> f = b =>
            {
                int t = 0, c = 0, w = 0, h;
                for (; w < b.Width; ++w)
                    for (h = 0; h < b.Height; ++t)
                    {
                        var p = b.GetPixel(w, h++);

                        if (p.R > 191 & p.G > 191 & p.B > 191)
                            c++;
                    }

                return (int)(c / (t + 0d) * 8);
            };

            string[] testCases =
            {
                @"Appearance_of_sky_for_weather_forecast,_Dhaka,_Bangladesh.JPG",
                @"spanish-sky.jpeg",
                @"why-is-sky-blue-1.jpg",
            };

            foreach (string testCase in testCases)
            {
                using (Bitmap bitmap = new Bitmap(testCase))
                {
                    Console.WriteLine(f(bitmap));
                }
            }

            Console.ReadLine();
        }
    }
}

TheLethalCoder

Posted 2017-07-28T13:01:30.040

Reputation: 6 930

for(h=0 h<b.Height;++t) I think you missed a semi-colon there – user41805 – 2017-07-28T17:59:46.413

2You can replace the /0.125 with *8 at the end to save a few bytes. – Ian H. – 2017-07-29T13:10:05.320

@Cowsquack I'd deleted the semi colon instead of the space! Fixed now.. – TheLethalCoder – 2017-07-31T07:54:01.110

3

C#, 313 bytes

namespace System.Drawing.Imaging{b=>{unsafe{int t=0,c=0,y=0,x,w=b.Width,h=b.Height;var d=b.LockBits(new Rectangle(0,0,w,h),(ImageLockMode)1,(PixelFormat)137224);for(;y<h;++y){var r=(byte*)d.Scan0+y*d.Stride;for(x=0;x<w*3;++t)if(r[x++]>191&r[x++]>191&r[x++]>191)c++;}b.UnlockBits(d);return(int)(c/(t+0d)/0.125);}}}

Obviously longer than my other answer but this one uses LockBits and unsafe code to directly access the image in memory; as such it is incredibly quick. I could probably remove the call to UnlockBits but it's more correct with it there.

Full/Formatted version:

namespace System.Drawing.Imaging
{
    class P
    {
        static void Main()
        {
            Func<Bitmap, int> f = b =>
            {
                unsafe
                {
                    int t = 0, c = 0, y = 0, x, w = b.Width, h = b.Height;

                    var d = b.LockBits(new Rectangle(0, 0, w, h), (ImageLockMode)1, (PixelFormat)137224);
                    for (; y < h; ++y)
                    {
                        var r = (byte*)d.Scan0 + y * d.Stride;

                        for (x = 0; x < w * 3; ++t)
                            if (r[x++] > 191 & r[x++] > 191 & r[x++] > 191)
                                c++;
                    }
                    b.UnlockBits(d);

                    return (int)(c / (t + 0d) / 0.125);
                }
            };

            string[] testCases =
            {
                @"Appearance_of_sky_for_weather_forecast,_Dhaka,_Bangladesh.JPG",
                @"spanish-sky.jpeg",
                @"why-is-sky-blue-1.jpg",
            };

            foreach (string testCase in testCases)
            {
                using (Bitmap bitmap = new Bitmap(testCase))
                {
                    Console.WriteLine(f(bitmap));
                }
            }

            Console.ReadLine();
        }
    }
}

TheLethalCoder

Posted 2017-07-28T13:01:30.040

Reputation: 6 930

3

PowerShell, 200 bytes

$a=New-Object System.Drawing.Bitmap $args[0]
0..($a.Height-1)|%{$h=$_;0..($a.Width-1)|%{$i+=(("$($a.GetPixel($_,$h)|select R,G,B)"|iex)['R','G','B']-ge192).count-eq3}}
[int]($i/($a.Height*$a.Width)*8)

Gets input $args[0] as the full image file-path, constructs a New Bitmap object into $a. This is just the internal object name; it supports JPG, PNG, etc.

We then double-loop through the .height and then the .width of the image, touching each pixel. We pull out the R,G,B values and then select those that are -greaterthanorequal to 192 and make sure that count is 3 (i.e., all of them are white-ish). That Boolean result is added into our accumulator $i.

We then divide out to get the percentage, multiply it by 8 to get the number of oktas, and then [int] to get just an integer output. (Note that this performs Banker's Rounding -- if that's not allowed it'll be several more bytes to change the rounding method.)

AdmBorkBork

Posted 2017-07-28T13:01:30.040

Reputation: 41 581

2

dc, 74 bytes

???*sa?[1+]ss[r1+r]st[?191<s]su0ddsd[0luxluxlux3=t1+dla>r]dsrxr8*la2/+la/p

Input is taken as a P3 ppm file, with all whitespace as newlines. Output is to STDOUT.

Try it online!

poi830

Posted 2017-07-28T13:01:30.040

Reputation: 1 265

2

JavaScript, 83 77 bytes

-6 bytes by ETHproductions

f=i=>(z=a=b=0,i.map(e=>{z=e<192||z;(++b%4)||((z||(a+=64))&&(z=0))}),a/b+1>>1)

Inputs

Picture #1

Picture #2

Picture #3

Picture #4

Demo

f=i=>(z=a=b=0,i.map(e=>{z=e<192||z;(++b%4)||((z||(a+=64))&&(z=0))}),a/b+1>>1)


window.onload = function(){
 var input = document.getElementById('input');
 input.addEventListener('change', handleFiles);
}
function handleFiles(e){
 var canvas = document.getElementById('canvas')
 var ctx = canvas.getContext('2d');
 var img = new Image;
 img.onload = function(){
  canvas.height = img.height
  canvas.width = img.width
  ctx.drawImage(img, 0,0);
  data = ctx.getImageData(0,0,canvas.width,canvas.height).data;
  console.log(f(data))//invoke f, send imageData as input
 }
 img.src = URL.createObjectURL(e.target.files[0]);
}
<input type="file" id="input"/><br>
<canvas id="canvas" style="max-width:400px"/>

Евгений Новиков

Posted 2017-07-28T13:01:30.040

Reputation: 987

1Very nice solution. A handy trick with ES6 arrow functions is to wrap everything in parentheses, separated by commas (a=>(b,c,d)) instead of doing a=>{b;c;return d} or a=>eval("b;c;d"). This works unless you have a loop of some sort, in which case you'll probably be best off using the eval method. – ETHproductions – 2017-07-28T22:17:19.250

2

C (POSIX), 103 bytes

Assumes input as BMP file on stdin.

b,c,i,m=0xc0c0c0;main(){lseek(0,54,0);for(;read(0,&b,3);c+=(b&m)==m,i++);printf("%d",(i+16*c)/(2*i));}

yoann

Posted 2017-07-28T13:01:30.040

Reputation: 640

2

x86 Machine Code, 34 bytes

51
31 D2
AD
F7 D0
25 C0 C0 C0 00
75 01
42
E2 F3
C1 E2 03
DB 04 24
52
DB 04 24
DE F1
DB 1C 24
58
5A
C3

These bytes of code define a function that takes a bitmap input and returns an integer value indicating its oktas. As in C, arrays (like bitmaps) are represented as a pointer to the first element and a size/length. Thus, this function takes two parameters: the total number of pixels in the bitmap (rows × columns) and a pointer to the bitmap itself.

This code uses a custom register-based calling convention, where the bitmap pointer is passed in the ESI register and the bitmap size is passed in the ECX register. The result (oktas) is, per usual, returned in EAX.

As already stated above, the input is taken as a bitmap. Specifically, a 32-bpp format is used, in a little-endian format, but the alpha channel (highest-order byte) is ignored. This simplifies a lot of things, allowing us to simply iterate through each pixel and check its 32-bit RGB color value. A clever optimization is also used here. Instead of isolating each color component and checking whether it is >= 192, we just mask the entire 32-bit value by 0xC0C0C0 and test whether the result is >= 0xC0C0C0. This will evaluate to true for all "cloud" colors, and false for all "sky" (non-cloud) colors. Well, I thought it was clever! :-) It certainly saves a large number of bytes.

Therefore, in order to test this code, you will need to convert the input images to 32-bpp bitmaps. You cannot use Windows Paint for this, because it supports maximum of 24 bits-per-pixel. However, there are a number of other software solutions that can do it, such as Adobe Photoshop. I used this free tool, which converts a PNG to a 32-bpp BMP on Windows, meaning that you need only convert from JPEG to PNG (which Paint can do).

Other assumptions that I posit are eminently reasonable:

  • The bitmap is assumed to have a size greater than 0 (i.e., it is assumed to contain at least one pixel). This is reasonable because, when they sky is null, we have bigger problems than meteorology.
  • The direction flag (DF) is assumed to be clear so that we will iterate correctly through the bitmap using the LODSD instruction. This is the same assumption made by most x86 calling conventions, so it seems fair. If you don't like it, add 1 byte to the count for a CLD instruction.
  • The rounding mode for the x87 FPU is assumed to be set to round-to-nearest-even. This ensures that we get the correct behavior when we convert the number of oktas from a floating-point temporary to the final integer result, as verified by test case #4. This assumption is reasonable because this is the default state for the FPU and it is required to be maintained even in C code (where truncation is the default rounding behavior, forcing compilers that wish to be standards-compliant to generate inefficient code that changes the rounding mode, does the conversion, and then changes the rounding mode back).

Ungolfed assembly mnemonics:

; int ComputeOktas(void*    bmpBits  /* ESI */,
;                  uint32_t bmpSize  /* ECX */);
   push  ecx                  ; save size on stack
   xor   edx, edx             ; EDX = 0 (cloudy pixel counter)

CheckPixels:
   lodsd                      ; EAX = DS:[ESI]; ESI += 4
   not   eax
   and   eax, 0x00C0C0C0
   jnz   NotCloudy
   inc   edx
NotCloudy:
   loop  CheckPixels          ; ECX -= 1; loop if ECX > 0

   shl    edx, 3              ; counter *= 8
   fild   DWORD PTR [esp]     ; load original size from stack
   push   edx
   fild   DWORD PTR [esp]     ; load counter from stack
   fdivrp st(1), st(0)        ; ST(0) = counter*8 / size
   fistp  DWORD PTR [esp]     ; convert to integer, rounding to nearest even
   pop    eax                 ; load result
   pop    edx
   ret

Surely you haven't made it all this way down and are still wondering how the code works? :-)
Well, it's pretty simple. We just iterate through the bitmap one 32-bit value at a time, checking to see if that pixel RGB value is "cloudy" or "not cloudy". If it's cloudy, we increment our pre-zeroed counter. At the end, we compute: cloudy pixelstotal pixels × 8
(which is equivalent to: cloudy pixelstotal pixels &div; 0.125).

I can't include a TIO link for this because of the need for input images. I can, however, provide you with the harness I used to test this on Windows:

#include <stdio.h>
#include <assert.h>
#include <Windows.h>

int main()
{
   // Load bitmap as a DIB section under Windows, ensuring device-neutrality
   // and providing us direct access to its bits.
   HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,
                                        TEXT("C:\\...\\test1.bmp"),
                                        IMAGE_BITMAP,
                                        0, 0,
                                        LR_LOADFROMFILE  | LR_CREATEDIBSECTION);
   assert(hBitmap != NULL);

   // Get the bitmap's bits and attributes.
   DIBSECTION dib;
   GetObject(hBitmap, sizeof(dib), &dib);
   assert(dib.dsBm.bmBitsPixel == 32);
   uint32_t cx = dib.dsBm.bmWidth;
   uint32_t cy = abs(dib.dsBm.bmHeight);
   uint32_t sz = cx * cy;
   assert(sz > 0);

   int oktas = ComputeOktas(sz, dib.dsBm.bmBits);

   printf("%d\n", oktas);

   return 0;
}

Be careful with this, though! As defined above, ComputeOktas uses a custom calling convention, which a C compiler will not respect. You need to add code at the top of the assembly language procedure to load values from the stack into the expected registers, e.g.:

mov  ecx, DWORD PTR [bmpSize]
mov  esi, DWORD PTR [bmpBits]

Cody Gray

Posted 2017-07-28T13:01:30.040

Reputation: 2 639

1

Mathematica 89 bytes

The following binarizes the picture and determines the fraction of clouds, i.e. white pixels. Then it determines how many times .125 fits into the result. It returns the floor of that value.

o@i_:=⌊8Tr@Flatten[ImageData@MorphologicalBinarize[i,.932],1]/Times@@ImageDimensions@i⌋

DavidC

Posted 2017-07-28T13:01:30.040

Reputation: 24 524

1

JavaScript (ES6), 218 bytes

(a,c=document.createElement`canvas`,w=c.width=a.width,h=c.height=a.height,x=c.getContext`2d`)=>x.drawImage(a,0,0)||x.getImageData(0,0,w,h).data.reduce((o,_,i,d)=>o+(i%4|d[i++]<192|d[i++]<192|d[i]<192?0:1),0)/w/h*8+.5|0

Takes an Image object as input, which can be created from an <image> element.

Test it out here on CodePen!

Alternative

If input can be taken as flat array of RGBA values, with dimensions: 82 bytes

(d,w,h)=>d.reduce((o,_,i)=>o+(i%4|d[i++]<192|d[i++]<192|d[i]<192?0:1),0)/w/h*8+.5|0

This input format is very similar to what this answer on meta suggests.

Justin Mariner

Posted 2017-07-28T13:01:30.040

Reputation: 4 746