Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Commit

Permalink
Use different Docker image for blender verification (#3844)
Browse files Browse the repository at this point in the history
* Use different Docker image for blender verification

* test cleanup

* test failing docker

* Update scripts to newer version

* Fix to pixel shifts

* Run docker tests on linux only

* freeze scikit-learn
  • Loading branch information
Krigpl authored Feb 19, 2019
1 parent 8df94d1 commit 8c65d74
Show file tree
Hide file tree
Showing 33 changed files with 1,678 additions and 1,217 deletions.
400 changes: 0 additions & 400 deletions apps/blender/blender_reference_generator.py

This file was deleted.

36 changes: 36 additions & 0 deletions apps/blender/resources/images/blender_verifier.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM golemfactory/blender:1.8

# Install scripts requirements first, then add scripts.
ADD scripts_verifier/requirements.txt /golem/work/
ADD scripts_verifier/copy.sh /golem/

# Install any needed packages specified in requirements.txt
RUN set +x \
&& apt-get update \
&& apt-get install -y libglib2.0-0 \
&& apt-get install -y g++ \
&& apt-get install -y libsm6 \
&& apt-get install -y libxrender1 \
&& apt-get install -y wget \
&& apt-get install -y zlib1g-dev \
&& apt-get install -y libopenexr-dev \
&& /golem/install_py_libs.sh /golem/work/requirements.txt \
&& /golem/copy.sh \
&& apt-get remove -y libopenexr-dev \
&& apt-get remove -y zlib1g-dev \
&& apt-get remove -y wget \
&& apt-get remove -y libxrender1 \
&& apt-get remove -y libsm6 \
&& apt-get remove -y g++ \
&& apt-get remove -y libglib2.0-0 \
&& apt-get clean \
&& apt-get -y autoremove \
&& rm -rf /var/lib/apt/lists/*

ENV PYTHONPATH=/golem/scripts:/golem/scripts_verifier:/golem:$PYTHONPATH

# Create symbolic link to python. I don't know where, something removes it.
RUN ln -s /usr/bin/python3.6 /usr/bin/python3

RUN mkdir /golem/scripts_verifier
ADD scripts_verifier/ /golem/scripts_verifier
Empty file.
9 changes: 9 additions & 0 deletions apps/blender/resources/images/scripts_verifier/copy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cp /usr/lib/x86_64-linux-gnu/libXrender.so.1 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libSM.so.6 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libgthread-2.0.so.0 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libICE.so.6 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libIex-2_2.so.12 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libIlmImf-2_2.so.22 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libHalf.so.12 /usr/local/lib/.
cp /usr/lib/x86_64-linux-gnu/libIlmThread-2_2.so.12 /usr/local/lib/.
173 changes: 173 additions & 0 deletions apps/blender/resources/images/scripts_verifier/crop_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import os
import numpy
import math
import random
from typing import Dict, Tuple, List, Optional

WORK_DIR = "/golem/work"
OUTPUT_DIR = "/golem/output"

class Region:

def __init__(self, left: float, top: float, right: float, bottom: float):
self.left = left
self.right = right
self.top = top
self.bottom = bottom

class PixelRegion:

def __init__(self, left: int, top: int, right: int, bottom: int):
self.left = left
self.right = right
self.top = top
self.bottom = bottom

class SubImage:

CROP_RELATIVE_SIZE = 0.1
PIXEL_OFFSET = numpy.float32(0.5)
MIN_CROP_SIZE = 8

def __init__(self, region: Region, resolution: List[int]):
self.region = region
self.pixel_region = self.calculate_pixels(region, resolution[0], resolution[1])
self.width = self.pixel_region.right - self.pixel_region.left
self.height = self.pixel_region.top - self.pixel_region.bottom
self.resolution = resolution

def calculate_pixels(self, region: Region, width: int, height: int) -> None:
# This is how Blender is calculating pixel, check
# BlenderSync::get_buffer_params in blender_camera.cpp file
# BoundBox2D border = cam->border.clamp();
# params.full_x = (int)(border.left * (float)width);

# NOTE blender uses floats (single precision) while python operates on
# doubles
# Here numpy is used to emulate this loss of precision when assigning
# double to float:
left = math.floor(
numpy.float32(region.left) * numpy.float32(width) +
SubImage.PIXEL_OFFSET)

right = math.floor(
numpy.float32(region.right) * numpy.float32(width) +
SubImage.PIXEL_OFFSET)

# NOTE we are exchanging here top with bottom, because borders
# in blender are in OpenGL UV coordinate system (left, bottom is 0,0)
# where pixel values are for use in classic coordinate system (left, top is 0,0)

top = math.floor(
numpy.float32(region.bottom) * numpy.float32(height) +
SubImage.PIXEL_OFFSET)

bottom = math.floor(
numpy.float32(region.top) * numpy.float32(height) +
SubImage.PIXEL_OFFSET)

print("Pixels left=%r, top=%r, right=%r, bottom=%r" %
(left, top, right, bottom))
return PixelRegion(left, top, right, bottom)

@staticmethod
def __calculate_crop_side_length(subtask_side_length: int) -> int:
calculated_length = int(
SubImage.CROP_RELATIVE_SIZE * subtask_side_length)

return max(SubImage.MIN_CROP_SIZE, calculated_length)

def get_default_crop_size(self) -> Tuple[int, int]:
x = self.__calculate_crop_side_length(self.width)
y = self.__calculate_crop_side_length(self.height)
return x, y

class Crop:

@staticmethod
def create_from_region(id: int, crop_region: Region, subimage: SubImage):
crop = Crop(id, subimage)
crop.crop_region = crop_region
crop.pixel_region = crop.subimage.calculate_pixels(crop_region,
subimage.width, subimage.height)
return crop

@staticmethod
def create_from_pixel_region(id: int, pixel_region: PixelRegion, subimage: SubImage):
crop = Crop(id, subimage)
crop.pixel_region = pixel_region
crop.crop_region = crop.calculate_borders(pixel_region, subimage.resolution[0], subimage.resolution[1])
return crop

def __init__(self, id: int, subimage: SubImage):
self.id = id
self.subimage = subimage
self.pixel_region = None
self.crop_region = None

def get_relative_top_left(self) \
-> Tuple[int, int]:
# get top left corner of crop in relation to particular subimage
print("Sumimag top=%r - crop.top=%r" % (self.subimage.region.top, self.pixel_region.top))
y = self.subimage.pixel_region.top - self.pixel_region.top
print("X=%r, Y=%r" % (self.pixel_region.left, y))
return self.pixel_region.left, y

def calculate_borders(self, pixel_region: PixelRegion, width: int, height: int):

left = numpy.float32(
(numpy.float32(pixel_region.left) + SubImage.PIXEL_OFFSET) /
numpy.float32(width))

right = numpy.float32(
(numpy.float32(pixel_region.right) + SubImage.PIXEL_OFFSET) /
numpy.float32(width))

bottom = numpy.float32(
(numpy.float32(pixel_region.top) + SubImage.PIXEL_OFFSET) /
numpy.float32(height))

top = numpy.float32(
(numpy.float32(pixel_region.bottom) + SubImage.PIXEL_OFFSET) /
numpy.float32(height))

return Region(left, top, right, bottom)

def generate_single_random_crop_data(subimage: SubImage, crop_size_px: Tuple[int, int], id: int) \
-> Crop:

crop_horizontal_pixel_coordinates = _get_random_interval_within_boundaries(
subimage.pixel_region.left,
subimage.pixel_region.right,
crop_size_px[0])

crop_vertical_pixel_coordinates = _get_random_interval_within_boundaries(
subimage.pixel_region.bottom,
subimage.pixel_region.top,
crop_size_px[1])

crop = Crop.create_from_pixel_region(id, PixelRegion(
crop_horizontal_pixel_coordinates[0],
crop_vertical_pixel_coordinates[1],
crop_horizontal_pixel_coordinates[1],
crop_vertical_pixel_coordinates[0]), subimage)

return crop

def _get_random_interval_within_boundaries(begin: int,
end: int,
interval_length: int) \
-> Tuple[int, int]:

# survive in edge cases
end -= 1
begin += 1

print("begin %r, end %r" % (begin, end))

max_possible_interval_end = (end - interval_length)
if max_possible_interval_end < 0:
raise Exception("Subtask is too small for reliable verification")
interval_begin = random.randint(begin, max_possible_interval_end)
interval_end = interval_begin + interval_length
return interval_begin, interval_end
40 changes: 40 additions & 0 deletions apps/blender/resources/images/scripts_verifier/decision_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import numpy
from sklearn.externals import joblib


## ======================= ##
##
class DecisionTree:

## ======================= ##
##
def __init__( self, clf ):
self.clf = clf

## ======================= ##
##
@staticmethod
def load( file ):
data = joblib.load( file )
tree = DecisionTree( data[0] )

return tree, data[1]

## ======================= ##
##
def classify_with_feature_vector(self, feature_vector, labels):

numpy_format = []
for label in labels:
numpy_format.append((label, numpy.float64))

converted_features = numpy.zeros(1, dtype=numpy_format)
for name in converted_features.dtype.names:
converted_features[name] = feature_vector[name]

samples = converted_features.view(numpy.float64).reshape(
converted_features.shape + (-1,))

results = self.clf.predict(samples)

return numpy.array(results)
63 changes: 63 additions & 0 deletions apps/blender/resources/images/scripts_verifier/edges.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from PIL import Image, ImageFilter
import numpy
from skimage import compare_mse


import sys


## ======================= ##
##
class MetricEdgeFactor:


## ======================= ##
##
@staticmethod
def compute_metrics( image1, image2 ):

image1 = image1.convert("RGB")
image2 = image2.convert("RGB")

edged_image1 = image1.filter( ImageFilter.FIND_EDGES )
edged_image2 = image2.filter( ImageFilter.FIND_EDGES )

np_image1 = numpy.array( edged_image1 )
np_image2 = numpy.array( edged_image2 )

ref_edge_factor = numpy.mean( np_image1 )
comp_edge_factor = numpy.mean( np_image2 )

edge_factor = compare_mse( np_image1, np_image2 )

result = dict()
result[ "ref_edge_factor" ] = ref_edge_factor
result[ "comp_edge_factor" ] = comp_edge_factor
result[ "edge_difference" ] = edge_factor

return result

## ======================= ##
##
@staticmethod
def get_labels():
return [ "ref_edge_factor", "comp_edge_factor", "edge_difference" ]

## ======================= ##
##
def run():

first_img = sys.argv[ 1 ]
second_img = sys.argv[ 2 ]

first_img = Image.open( first_img )
second_img = Image.open( second_img )

ssim = MetricEdgeFactor()

print( ssim.compute_metrics( first_img, second_img ) )



if __name__ == "__main__":
run()
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import cv2
import numpy
from PIL import Image
import sys


class MetricHistogramsCorrelation:

@staticmethod
def compute_metrics( image1, image2):
if image1.size != image2.size:
raise Exception("Image sizes differ")
opencv_image_1 = cv2.cvtColor(numpy.array(image1), cv2.COLOR_RGB2BGR)
opencv_image_2 = cv2.cvtColor(numpy.array(image2), cv2.COLOR_RGB2BGR)
return {"histograms_correlation": MetricHistogramsCorrelation.compare_histograms(opencv_image_1, opencv_image_2)}

@staticmethod
def get_labels():
return ["histograms_correlation"]

@staticmethod
def get_number_of_pixels(image):
height, width = image.shape[:2]
return height * width

@staticmethod
def calculate_normalized_histogram(image):
number_of_bins = 256
channels_number = 3 # because of conversion from PIL to opencv
histogram = cv2.calcHist([image],
range(channels_number),
None,
[number_of_bins] * channels_number,
[0, 256] * channels_number)
cv2.normalize(histogram, histogram, 0, 256, cv2.NORM_MINMAX)
return histogram

@staticmethod
def compare_histograms(image_a, image_b):
histogram_a = MetricHistogramsCorrelation.calculate_normalized_histogram(image_a)
histogram_b = MetricHistogramsCorrelation.calculate_normalized_histogram(image_b)
result = cv2.compareHist(histogram_a, histogram_b, cv2.HISTCMP_CORREL)
return result


def run():
first_img = Image.open(sys.argv[1])
second_img = Image.open(sys.argv[2])

histograms_correlation_metric = MetricHistogramsCorrelation()

print(histograms_correlation_metric.compute_metrics(first_img, second_img))


if __name__ == "__main__":
run()
Loading

0 comments on commit 8c65d74

Please sign in to comment.