Problem Statement

You are given an image representing a scanned OMR answer sheet. Each image contains a grid of circles, with each row representing one question and each column representing a possible answer (A, B, C, D). Some circles are filled to indicate the selected answer by the candidate.

Your task is to write a program that takes the image as input, along with an array containing the correct answers for each question and returns a score based on the number of correct answers. For each correct answer, score will be incremented by 1 point. Each question has only 1 correct answer.

The filled circles in the images can be either fully filled (360 degrees filled) or partially filled. A fully filled circle represents a definite answer, while a partially filled circle represents a probable answer. If a circle is filled more than 75% (270 degrees filled) of the area, it should be considered as a definite answer.
Sample OMR images

    For the below image, when evaluating with the expected answer as D A D C, the score is evaluated as 4

    image

    For the below image, where partial circles are filled, when evaluating with expected answer as C D D D, the score is evaluated as 3

    image

Note

Few things to keep in mind while evaluating the score

    A filled circle is considered definite answer when 75% of the circle area is filled, which means 270 degress or more must be filled to consider it as a definite answer.

    If a candidate has marked more than one circle as definite answer for a question, then even if the definite answers contains the correct answer, no score will be provided for that question.

Input Format
Guidelines

    First line in the standard input represents the image to be used eg. omr_sheet_1.png
    Second line in the standard input represents the correct answer for the omr sheet, separated by a space

Sample Input

omr_sheet_1.png
D A D C
Reading the Input

You can read the input as standard input.

To load the image, you can load it as following:
Python

//read from standard input filename = input() // get file if os.path.isfile(filename):
# Open the file
with open(filename, 'r') as file:
...
Java

import java.io.File;
...
// read from standard input Scanner scanner = new Scanner(System.in); String query = scanner.nextLine(); // Get the image file
File file = new File("omr_sheet_1.png");

Constraints
Image constraints

    All Image dimensions are 500 x 500
    2 color present only, black & white.
    Every image has 4 Questions with 4 options.
    Every Question has only 1 correct question.

Output Format
Guidelines

    Please output only the score in standard output

Sample Output

3
For the below code only 1 test case is giving the wrong answer so dont introduce any external packages look at the points of ways where the answer could be wrong
import struct
import zlib
import sys
import numpy as np

def get_png_pixel_data(filename):
    # Read the PNG file
    with open(filename, 'rb') as f:
        # Check PNG signature
        signature = f.read(8)
        if signature != b'\x89PNG\r\n\x1a\n':
            raise ValueError("Not a valid PNG file")

        width = height = None
        image_data_chunks = []
        color_type = None
        bit_depth = None

        # Read chunks until we find IHDR (image header) and IDAT (image data)
        while True:
            # Each chunk starts with length (4 bytes) and type (4 bytes)
            chunk_header = f.read(8)
            if len(chunk_header) < 8:
                raise ValueError("Incomplete PNG file.")
            chunk_len, chunk_type = struct.unpack(">I4s", chunk_header)
            chunk_data = f.read(chunk_len)
            crc = f.read(4)  # CRC, not used here

            if chunk_type == b'IHDR':  # Header chunk
                if len(chunk_data) != 13:
                    raise ValueError("Invalid IHDR chunk size.")
                width, height, bit_depth, color_type, compression, filter_method, interlace = struct.unpack(">IIBBBBB", chunk_data)
                # Ensure image dimensions
                if width != 500 or height != 500:
                    raise ValueError("Only 500x500 images are supported")
                # Validate supported color types
                if color_type not in (0, 2):
                    raise ValueError("Unsupported PNG color type. Only Grayscale and RGB are supported.")
                # Validate bit depth
                if bit_depth != 8:
                    raise ValueError("Only 8-bit depth is supported.")
            elif chunk_type == b'IDAT':  # Image data chunk
                image_data_chunks.append(chunk_data)
            elif chunk_type == b'IEND':  # End of PNG file
                break
            else:
                # Skip other chunk types
                continue

        # Concatenate all IDAT chunks and decompress
        compressed_data = b''.join(image_data_chunks)
        try:
            decompressed_data = zlib.decompress(compressed_data)
        except zlib.error as e:
            raise ValueError(f"Zlib decompression failed: {e}")

        # Prepare to reconstruct image
        if color_type == 0:  # Grayscale
            bytes_per_pixel = 1
        elif color_type == 2:  # RGB
            bytes_per_pixel = 3
        else:
            raise ValueError("Unsupported PNG color type.")

        # Initialize image array
        image = np.zeros((height, width), dtype=np.uint8)

        # Each scanline starts with a filter type byte
        bytes_per_scanline = bytes_per_pixel * width
        expected_data_length = (bytes_per_scanline + 1) * height
        if len(decompressed_data) != expected_data_length:
            raise ValueError("Decompressed data size does not match expected size.")

        for y in range(height):
            # Calculate the start and end indices of the current scanline
            filter_type = decompressed_data[y * (bytes_per_scanline + 1)]
            scanline = decompressed_data[y * (bytes_per_scanline + 1) + 1 : (y + 1) * (bytes_per_scanline + 1)]
            
            if filter_type != 0:
                raise NotImplementedError(f"Only filter type 0 (None) is supported, found filter type {filter_type}.")

            if color_type == 0:  # Grayscale
                row = np.frombuffer(scanline, dtype=np.uint8)
                image[y, :] = row
            elif color_type == 2:  # RGB
                # Convert RGB to Grayscale using the luminosity formula
                row = np.frombuffer(scanline, dtype=np.uint8)
                r = row[0::3]
                g = row[1::3]
                b = row[2::3]
                grayscale = (0.299 * r + 0.587 * g + 0.114 * b).astype(np.uint8)
                image[y, :] = grayscale

        # Convert to a list of lists for compatibility with the existing code
        grayscale_image = image.tolist()

        return grayscale_image

def main():
    # Read input lines
    input_lines = sys.stdin.read().splitlines()
    if len(input_lines) < 2:
        print(0)
        return

    filename = input_lines[0].strip()
    correct_answers = input_lines[1].strip().split()

    # Validate the correct_answers length
    if len(correct_answers) != 4:
        print(0)
        return

    # Mapping from option letter to index
    char_to_num = {'A': 0, 'B': 1, 'C': 2, 'D': 3}

    # Convert the string to an array of numbers
    try:
        num_array = [char_to_num[char.upper()] for char in correct_answers]
    except KeyError:
        # Invalid answer option provided
        print(0)
        return

    # Load the image as grayscale using the updated function
    try:
        grayscale_image = get_png_pixel_data(filename)
    except Exception as e:
        # If any error occurs during image processing, return score 0
        # You can uncomment the following line for debugging purposes
        # print(f"Error processing image: {e}")
        print(0)
        return

    # Assuming a 500x500 grayscale image
    image_height = len(grayscale_image)
    image_width = len(grayscale_image[0])

    window_size = 32

    # Adjust column and row start points based on image analysis or predefined layout
    # These values are based on the sample description; adjust them if necessary
    col_start_pts = [32, 162, 296, 432]
    row_start_pts = [36, 168, 300, 432]

    correct_ans = 0

    # Loop through the image in 32x32 blocks
    for idx, row_start in enumerate(row_start_pts):
        attempted = 0
        row_score = 0
        
        for idy, col_start in enumerate(col_start_pts):
            black_pixel_count = 0
            total_pixels = window_size * window_size

            # Traverse through the 32x32 block
            for row in range(row_start, min(row_start + window_size, image_height)):
                for col in range(col_start, min(col_start + window_size, image_width)):
                    # Check if the pixel is black (assuming black is value 0)
                    if grayscale_image[row][col] == 0:
                        black_pixel_count += 1

            # Calculate the percentage of black pixels in the current 32x32 block
            black_percentage = (black_pixel_count / total_pixels) * 100
            if black_percentage > 50:
                attempted+=1
                
                if num_array[idx] == idy:
                    row_score += 1    
                elif attempted >1:
                    row_score = 0
                
        correct_ans+=row_score

            # Debugging information (optional)
            # print(f"Block ({col_start}, {row_start}) - Black Pixel %: {black_percentage:.2f}%")

    print(correct_ans)

if __name__ == "__main__":
    main()