山内セミナー(2018/12/12)

関連サイトと資料

ROIの指定とマスク画像生成

tamagoyaki.jpg

designROI1.py
import cv2
import numpy as numpy
import os
import PolygonRoi
	
def isClose(x0, y0, x, y, th):
    xx, yy = x0 - x, y0 - y
    if (xx*xx + yy*yy) < th*th:
        return True
    return False
	
def saveROI(json_filename, xpoints, ypoints):
    n = len(xpoints)
    fp = open(json_filename, 'w')
    fp.write('{\n')
    fp.write('   "num": {0},\n'.format(n))
    fp.write('   "axis": [\n')
    for i in range(n):
        fp.write('            { "x": ' + '{0}, "y": {1}'.format(xpoints[i], ypoints[i]) + ' }')
        if i < (n - 1):
            fp.write(',\n')
        else:
            fp.write('\n')
    fp.write('   ]\n')
    fp.write('}\n')
    fp.close()
	
def saveMaskImg(img_filename, xpoints, ypoints, width, height):
    roi = PolygonRoi.PolygonRoi(xpoints, ypoints)
    maskImg = roi.createMask(width, height)
    cv2.imwrite(img_filename, maskImg)
	
def onmouse(event, x, y, flags, param):
    global r
    global img
    global c0
    global c1
    global c2
    global xp
    global yp
    global finished
	
    if finished == False and event == cv2.EVENT_LBUTTONUP:
        xp.append(x)
        yp.append(y)
        if len(xp) >= 3 and isClose(x, y, xp[0], yp[0], r) == True:
            cv2.circle(img, (xp[0], yp[0]), r, c2, -1)
            cv2.line(img, (xp[-2], yp[-2]), (xp[-1], yp[-1]), c0, 2)
            xp.pop(-1)
            yp.pop(-1)
            finished = True
        else:
            cv2.circle(img, (x, y), r, c1, -1)
            if len(xp) >= 2:
                cv2.line(img, (xp[-2], yp[-2]), (xp[-1], yp[-1]), c0, 2)
	
if __name__ == '__main__':
    target = 'tamagoyaki.jpg'
    base, ext = os.path.splitext(target)
    json_name = base + '_ROI.json'
    mask_name = base + '_mask.png'
    roi_name = base + '_ROI.png'
	
    r = 5
    c0 = (0, 0, 0)
    c1 = (255, 0, 0)
    c2 = (0, 0, 255)
    finished = False
    xp = []
    yp = []
	
    img = cv2.imread(target, cv2.IMREAD_COLOR)
    cv2.namedWindow(target)
    cv2.setMouseCallback(target, onmouse)
	
    while True:
        cv2.imshow(target, img)
        key = cv2.waitKey(17)
        if key == ord('q'):
            break
    
    if finished == True:
        h, w, _ = img.shape
        cv2.imwrite(roi_name, img)
        saveROI(json_name, xp, yp)
        saveMaskImg(mask_name, xp, yp, w, h)
    
    cv2.destroyAllWindows()

PolygonRoi.py
import sys
import cv2
import numpy as np
	
class PolygonRoi:
    def __init__(self, xp_, yp_):
        self.xp = xp_
        self.yp = yp_
        self.n = len(xp_) 
    
    @classmethod
    def area2(cls, x1, y1, x2, y2, x3, y3):
        return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)
    
    @classmethod
    def between(cls, x1, y1, x2, y2, x3, y3):
        if x1 != x2:
            return (x1 <= x3 and x3 <= x2) or (x1 >= x3 and x3 >= x2)
        else:
            return (y1 <= y3 and y3 <= y2) or (y1 >= y3 and y3 >= y2)
    
    @classmethod
    def linesIntersect(cls, x1, y1, x2, y2, x3, y3, x4, y4):
        aa1 = PolygonRoi.area2(x1, y1, x2, y2, x3, y3)
        aa2 = PolygonRoi.area2(x1, y1, x2, y2, x4, y4)
        aa3 = PolygonRoi.area2(x3, y3, x4, y4, x1, y1)
        aa4 = PolygonRoi.area2(x3, y3, x4, y4, x2, y2)
	
        if aa1 == 0.0:
            if PolygonRoi.between(x1, y1, x2, y2, x3, y3):
                return True
            else:
                if PolygonRoi.area2(x1, y1, x2, y2, x4, y4) == 0.0:
                    a1 = PolygonRoi.between(x3, y3, x4, y4, x1, y1)
                    b1 = PolygonRoi.between(x3, y3, x4, y4, x2, y2)
                    return a1 or b1
                else:
                    return False
        elif aa2 == 0.0:
            return PolygonRoi.between(x1, y1, x2, y2, x4, y4)
        
        if aa3 == 0.0:
            if PolygonRoi.between(x3, y3, x4, y4, x1, y1):
                return True
            else:
                if PolygonRoi.area2(x3, y3, x4, y4, x2, y2) == 0.0:
                    a2 = PolygonRoi.between(x1, y1, x2, y2, x3, y3)
                    b2 = PolygonRoi.between(x1, y1, x2, y2, x4, y4)
                    return a2 or b2
                else:
                    return False
        elif aa4 == 0.0:
            return PolygonRoi.between(x3, y3, x4, y4, x2, y2)
        else:
            return ((aa1 > 0.0) ^ (aa2 > 0.0)) and ((aa3 > 0.0) ^ (aa4 > 0.0))
    
    def evaluateCrossings(self, x, y, distance):
        crossings = 0
        epsilon = 1e-7
        x0 = self.xp[0] - x
        y0 = self.yp[0] - y

        for i in range(1, self.n):
            x1 = self.xp[i] - x
            y1 = self.yp[i] - y

            if y0 == 0.0:
                y0 -= epsilon
            
            if y1 == 0.0:
                y1 -= epsilon
            
            if y0 * y1 < 0:
                if PolygonRoi.linesIntersect(x0, y0, x1, y1, epsilon, 0.0, distance, 0.0):
                    crossings += 1
            
            x0 = self.xp[i] - x
            y0 = self.yp[i] - y
        
        x1 = self.xp[0] - x
        y1 = self.yp[0] - y

        if y0 == 0.0:
            y0 -= epsilon
        
        if y1 == 0.0:
            y1 -= epsilon
        
        if y0 * y1 < 0:
            if PolygonRoi.linesIntersect(x0, y0, x1, y1, epsilon, 0.0, distance, 0.0):
                crossings += 1
        
        return crossings
    
    def contains(self, x, y):
        return (self.evaluateCrossings(x, y, sys.float_info.max / 10.0) & 1) != 0
    
    def createMask(self, width, height):
        img = np.zeros((height, width), np.uint8)
        for y in range(height):
            for x in range(width):
                if self.contains(x, y):
                    img[y, x] = 255
        
        return img

tamagoyaki_ROI.json
{
   "num": 10,
   "axis": [
            { "x": 90, "y": 20 },
            { "x": 171, "y": 55 },
            { "x": 185, "y": 79 },
            { "x": 179, "y": 115 },
            { "x": 161, "y": 129 },
            { "x": 117, "y": 159 },
            { "x": 27, "y": 112 },
            { "x": 25, "y": 68 },
            { "x": 50, "y": 52 },
            { "x": 56, "y": 36 }
   ]
}

tamagoyaki_ROI.png

tamagoyaki_mask.png