I'm developing an android app to recognize text in particular plate, as in photo here:
I have to recognize the texts in white (e.g. near to "Mod."). I'm using Google ML Kit's text recognition APIs, but it fails. So, I'm using OpenCV to edit image but I don't know how to emphasize the (white) texts so OCR recognize it. I tried more stuff, like contrast, brightness, gamma correction, adaptive thresholding, but the cases vary a lot depending on how the photo is taken. Do you have any ideas?
Thank u very much.
I coded this example in Python (since OpenCV's SIFT in Android is paid) but you can still use this to understand how to solve it.
First I created this image as a template:
Step 1: Load images
""" 1. Load images """
# load image of plate
src_path = "nRHzD.jpg"
src = cv2.imread(src_path)
# load template of plate (to be looked for)
src_template_path = "nRHzD_template.jpg"
src_template = cv2.imread(src_template_path)
Step 2: Find the template using SIFT and perspective transformation
# convert images to gray scale
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
src_template_gray = cv2.cvtColor(src_template, cv2.COLOR_BGR2GRAY)
# use SIFT to find template
n_matches_min = 10
template_found, homography = find_template(src_gray, src_template_gray, n_matches_min)
warp = transform_perspective_and_crop(homography, src, src_gray, src_template)
warp_gray = cv2.cvtColor(warp, cv2.COLOR_BGR2GRAY)
warp_hsv = cv2.cvtColor(warp, cv2.COLOR_BGR2HSV)
template_hsv = cv2.cvtColor(src_template, cv2.COLOR_BGR2HSV)
Step 3: Find regions of interest (using the green parts of the template image)
green_hsv_lower_bound = [50, 250, 250]
green_hsv_upper_bound = [60, 255, 255]
mask_rois, mask_rois_img = crop_img_in_hsv_range(warp, template_hsv, green_hsv_lower_bound, green_hsv_upper_bound)
roi_list = separate_rois(mask_rois, warp_gray)
# sort the rois by distance to top right corner -> x (value[1]) + y (value[2])
roi_list = sorted(roi_list, key=lambda values: values[1]+values[2])
Step 4: Apply a Canny Edge detection to the rois (regions of interest)
for i, roi in enumerate(roi_list):
roi_img, roi_x_offset, roi_y_offset = roi
print("#roi:{} x:{} y:{}".format(i, roi_x_offset, roi_y_offset))
roi_img_blur_threshold = cv2.Canny(roi_img, 40, 200)
cv2.imshow("ROI image", roi_img_blur_threshold)
cv2.waitKey()
There are many ways for you to detect the digits, one of the easiest approaches is to run a K-Means Clustering on each of the contours.
Full code:
""" This code shows a way of getting the digit's edges in a pre-defined position (in green) """
import cv2
import numpy as np
def find_template(src_gray, src_template_gray, n_matches_min):
# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()
""" find grid using SIFT """
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(src_template_gray, None)
kp2, des2 = sift.detectAndCompute(src_gray, None)
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
# store all the good matches as per Lowe's ratio test.
good = []
for m,n in matches:
if m.distance < 0.7*n.distance:
good.append(m)
if len(good) > n_matches_min:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
matchesMask = mask.ravel().tolist()
h_template, w_template = src_template_gray.shape
pts = np.float32([[0, 0], [0, h_template - 1], [w_template - 1, h_template - 1], [w_template - 1,0]]).reshape(-1,1,2)
homography = cv2.perspectiveTransform(pts, M)
else:
print "Not enough matches are found - %d/%d" % (len(good), n_matches_min)
matchesMask = None
# show matches
draw_params = dict(matchColor = (0, 255, 0), # draw matches in green color
singlePointColor = None,
matchesMask = matchesMask, # draw only inliers
flags = 2)
if matchesMask:
src_gray_copy = src_gray.copy()
sift_matches = cv2.polylines(src_gray_copy, [np.int32(homography)], True, 255, 2, cv2.LINE_AA)
sift_matches = cv2.drawMatches(src_template_gray, kp1, src_gray_copy, kp2, good, None, **draw_params)
return sift_matches, homography
def transform_perspective_and_crop(homography, src, src_gray, src_template_gray):
""" get mask and contour of template """
mask_img_template = np.zeros(src_gray.shape, dtype=np.uint8)
mask_img_template = cv2.polylines(mask_img_template, [np.int32(homography)], True, 255, 1, cv2.LINE_AA)
_ret, contours, hierarchy = cv2.findContours(mask_img_template, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
template_contour = None
# approximate the contour
c = contours[0]
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# if our approximated contour has four points, then
# we can assume that we have found our template
warp = None
if len(approx) == 4:
template_contour = approx
cv2.drawContours(mask_img_template, [template_contour] , -1, (255,0,0), -1)
""" Transform perspective """
# now that we have our template contour, we need to determine
# the top-left, top-right, bottom-right, and bottom-left
# points so that we can later warp the image -- we'll start
# by reshaping our contour to be our finals and initializing
# our output rectangle in top-left, top-right, bottom-right,
# and bottom-left order
pts = template_contour.reshape(4, 2)
rect = np.zeros((4, 2), dtype = "float32")
# the top-left point has the smallest sum whereas the
# bottom-right has the largest sum
s = pts.sum(axis = 1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# compute the difference between the points -- the top-right
# will have the minumum difference and the bottom-left will
# have the maximum difference
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
# now that we have our rectangle of points, let's compute
# the width of our new image
(tl, tr, br, bl) = rect
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
# ...and now for the height of our new image
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
# take the maximum of the width and height values to reach
# our final dimensions
maxWidth = max(int(widthA), int(widthB))
maxHeight = max(int(heightA), int(heightB))
# construct our destination points which will be used to
# map the screen to a top-down, "birds eye" view
homography = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype = "float32")
# calculate the perspective transform matrix and warp
# the perspective to grab the screen
M = cv2.getPerspectiveTransform(rect, homography)
warp = cv2.warpPerspective(src, M, (maxWidth, maxHeight))
# resize warp
h_template, w_template, _n_channels = src_template_gray.shape
warp = cv2.resize(warp, (w_template, h_template), interpolation=cv2.INTER_AREA)
return warp
def crop_img_in_hsv_range(img, hsv, lower_bound, upper_bound):
mask = cv2.inRange(hsv, np.array(lower_bound), np.array(upper_bound))
# do an MORPH_OPEN (erosion followed by dilation) to remove isolated pixels
kernel = np.ones((5,5), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img, img, mask=mask)
return mask, res
def separate_rois(column_mask, img_gray):
# go through each of the boxes
# https://stackoverflow.com/questions/41592039/contouring-a-binary-mask-with-opencv-python
border = cv2.copyMakeBorder(column_mask, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=0)
_, contours, hierarchy = cv2.findContours(border, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE, offset=(-1, -1))
cell_list = []
for contour in contours:
cell_mask = np.zeros_like(img_gray) # Create mask where white is what we want, black otherwise
cv2.drawContours(cell_mask, [contour], -1, 255, -1) # Draw filled contour in mask
# turn that mask into a rectangle
(x,y,w,h) = cv2.boundingRect(contour)
#print("x:{} y:{} w:{} h:{}".format(x, y, w, h))
cv2.rectangle(cell_mask, (x, y), (x+w, y+h), 255, -1)
# copy the img_gray using that mask
img_tmp_region = cv2.bitwise_and(img_gray, img_gray, mask= cell_mask)
# Now crop
(y, x) = np.where(cell_mask == 255)
(top_y, top_x) = (np.min(y), np.min(x))
(bottom_y, bottom_x) = (np.max(y), np.max(x))
img_tmp_region = img_tmp_region[top_y:bottom_y+1, top_x:bottom_x+1]
cell_list.append([img_tmp_region, top_x, top_y])
return cell_list
""" 1. Load images """
# load image of plate
src_path = "nRHzD.jpg"
src = cv2.imread(src_path)
# load template of plate (to be looked for)
src_template_path = "nRHzD_template.jpg"
src_template = cv2.imread(src_template_path)
""" 2. Find the plate (using the template image) and crop it into a rectangle """
# convert images to gray scale
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
src_template_gray = cv2.cvtColor(src_template, cv2.COLOR_BGR2GRAY)
# use SIFT to find template
n_matches_min = 10
template_found, homography = find_template(src_gray, src_template_gray, n_matches_min)
warp = transform_perspective_and_crop(homography, src, src_gray, src_template)
warp_gray = cv2.cvtColor(warp, cv2.COLOR_BGR2GRAY)
warp_hsv = cv2.cvtColor(warp, cv2.COLOR_BGR2HSV)
template_hsv = cv2.cvtColor(src_template, cv2.COLOR_BGR2HSV)
""" 3. Find regions of interest (using the green parts of the template image) """
green_hsv_lower_bound = [50, 250, 250]
green_hsv_upper_bound = [60, 255, 255]
mask_rois, mask_rois_img = crop_img_in_hsv_range(warp, template_hsv, green_hsv_lower_bound, green_hsv_upper_bound)
roi_list = separate_rois(mask_rois, warp_gray)
# sort the rois by distance to top right corner -> x (value[1]) + y (value[2])
roi_list = sorted(roi_list, key=lambda values: values[1]+values[2])
""" 4. Apply a Canny Edge detection to the rois (regions of interest) """
for i, roi in enumerate(roi_list):
roi_img, roi_x_offset, roi_y_offset = roi
print("#roi:{} x:{} y:{}".format(i, roi_x_offset, roi_y_offset))
roi_img_blur_threshold = cv2.Canny(roi_img, 40, 200)
cv2.imshow("ROI image", roi_img_blur_threshold)
cv2.waitKey()
I'm wondering if I can clean up my code somehow using a list or call it from another class because I have a lot of exact Path2D coordinates just cluttering my paintComponent.
public void paintComponent(Graphics g)
{ Graphics2D g2=(Graphics2D)g;
super.paintComponent(g2);
//Background
Rectangle background = new Rectangle(0,0,getWidth(),getHeight());
Color skyBlue = new Color(135,206,235);
g2.setColor(skyBlue);
g2.fill (background);
Path2D bunny = new Path2D.Float();
bunny.moveTo(486.63,530.25);
bunny.lineTo(483.13,532.25);
bunny.lineTo(483.13,532.25);
bunny.lineTo(483.13,532.25);
bunny.lineTo(477.38,534.38);
bunny.lineTo(477.38,534.38);
bunny.lineTo(477.38,534.38);
bunny.lineTo(469.88,535.50);
bunny.lineTo(469.88,535.50);
bunny.lineTo(469.88,535.50);
bunny.lineTo(453.50,536.38);
bunny.lineTo(453.50,536.38);
bunny.lineTo(453.50,536.38);
bunny.lineTo(437.63,537.75);
bunny.lineTo(437.63,537.75);
bunny.lineTo(437.63,537.75);
bunny.lineTo(418.75,539.25);
bunny.lineTo(418.75,539.25);
bunny.lineTo(418.75,539.25);
bunny.lineTo(405.00,539.88);
bunny.lineTo(405.00,539.88);
bunny.lineTo(405.00,539.88);
bunny.lineTo(392.25,539.63);
bunny.lineTo(392.25,539.63);
bunny.lineTo(392.25,539.63);
bunny.lineTo(385.50,539.13);
bunny.lineTo(385.50,539.13);
bunny.lineTo(385.50,539.13);
bunny.lineTo(381.00,538.63);
bunny.lineTo(381.00,538.63);
bunny.lineTo(381.00,538.63);
bunny.lineTo(375.50,537.88);
bunny.lineTo(375.50,537.88);
bunny.lineTo(375.50,537.88);
bunny.lineTo(370.13,537.75);
bunny.lineTo(370.13,537.75);
bunny.lineTo(370.13,537.75);
bunny.lineTo(364.88,537.13);
bunny.lineTo(364.88,537.13);
bunny.lineTo(364.88,537.13);
bunny.lineTo(357.75,536.63);
bunny.lineTo(357.75,536.63);
bunny.lineTo(357.75,536.63);
bunny.lineTo(353.50,536.25);
bunny.lineTo(353.50,536.25);
bunny.lineTo(353.50,536.25);
bunny.lineTo(347.88,535.75);
bunny.lineTo(347.88,535.75);
bunny.lineTo(347.88,535.75);
bunny.lineTo(341.75,535.38);
bunny.lineTo(341.75,535.38);
bunny.lineTo(341.75,535.38);
bunny.lineTo(336.25,534.38);
bunny.lineTo(336.25,534.38);
bunny.lineTo(336.25,534.38);
bunny.lineTo(328.25,532.88);
bunny.lineTo(328.25,532.88);
bunny.lineTo(328.25,532.88);
bunny.lineTo(323.00,530.63);
bunny.lineTo(323.00,530.63);
bunny.lineTo(323.00,530.63);
bunny.lineTo(318.50,528.13);
bunny.lineTo(318.50,528.13);
bunny.lineTo(318.50,528.13);
bunny.lineTo(313.50,524.63);
bunny.lineTo(313.50,524.63);
bunny.lineTo(313.50,524.63);
bunny.lineTo(311.63,521.25);
bunny.lineTo(311.63,521.25);
bunny.lineTo(311.63,521.25);
bunny.lineTo(310.38,516.25);
bunny.lineTo(310.38,516.25);
bunny.lineTo(310.38,516.25);
bunny.lineTo(309.88,513.00);
bunny.lineTo(309.88,513.00);
bunny.lineTo(309.88,513.00);
bunny.lineTo(311.13,508.38);
bunny.lineTo(311.13,508.38);
bunny.lineTo(311.13,508.38);
bunny.lineTo(312.25,505.38);
bunny.lineTo(312.25,505.38);
bunny.lineTo(312.25,505.38);
bunny.lineTo(313.00,502.63);
bunny.lineTo(313.00,502.63);
bunny.lineTo(313.00,502.63);
bunny.lineTo(314.75,500.00);
bunny.lineTo(314.75,500.00);
bunny.lineTo(314.75,500.00);
bunny.lineTo(316.38,497.63);
bunny.lineTo(316.38,497.63);
bunny.lineTo(316.38,497.63);
bunny.lineTo(316.25,494.00);
bunny.lineTo(316.25,494.00);
bunny.lineTo(316.25,494.00);
bunny.lineTo(314.88,491.00);
bunny.lineTo(314.88,491.00);
bunny.lineTo(314.88,491.00);
bunny.lineTo(312.88,486.38);
bunny.lineTo(312.88,486.38);
bunny.lineTo(312.88,486.38);
bunny.lineTo(310.13,480.00);
bunny.lineTo(310.13,480.00);
bunny.lineTo(310.13,480.00);
bunny.lineTo(308.50,474.75);
bunny.lineTo(308.50,474.75);
bunny.lineTo(308.50,474.75);
bunny.lineTo(306.63,468.75);
bunny.lineTo(306.63,468.75);
bunny.lineTo(306.63,468.75);
bunny.lineTo(305.25,462.75);
bunny.lineTo(305.25,462.75);
bunny.lineTo(305.25,462.75);
bunny.lineTo(304.88,456.63);
bunny.lineTo(304.88,456.63);
bunny.lineTo(304.88,456.63);
bunny.lineTo(304.88,449.88);
bunny.lineTo(304.88,449.88);
bunny.lineTo(304.88,449.88);
bunny.lineTo(305.00,446.38);
bunny.lineTo(305.00,446.38);
bunny.lineTo(305.00,446.38);
bunny.lineTo(305.88,442.75);
bunny.lineTo(305.88,442.75);
bunny.lineTo(305.88,442.75);
bunny.lineTo(307.63,437.88);
bunny.lineTo(307.63,437.88);
bunny.lineTo(307.63,437.88);
bunny.lineTo(309.25,434.50);
bunny.lineTo(309.25,434.50);
bunny.lineTo(309.25,434.50);
bunny.lineTo(310.50,432.25);
bunny.lineTo(310.50,432.25);
bunny.lineTo(310.50,432.25);
bunny.lineTo(311.63,430.25);
bunny.lineTo(311.63,430.25);
bunny.lineTo(311.63,430.25);
bunny.lineTo(312.50,427.25);
bunny.lineTo(312.50,427.25);
bunny.lineTo(312.50,427.25);
bunny.lineTo(312.63,424.00);
bunny.lineTo(312.63,424.00);
bunny.lineTo(312.63,424.00);
bunny.lineTo(311.75,421.25);
bunny.lineTo(311.75,421.25);
bunny.lineTo(311.75,421.25);
bunny.lineTo(309.38,417.88);
bunny.lineTo(309.38,417.88);
bunny.lineTo(309.38,417.88);
bunny.lineTo(307.00,415.00);
bunny.lineTo(307.00,415.00);
bunny.lineTo(307.00,415.00);
bunny.lineTo(304.50,412.25);
bunny.lineTo(304.50,412.25);
bunny.lineTo(304.50,412.25);
bunny.lineTo(302.63,409.38);
bunny.lineTo(302.63,409.38);
bunny.lineTo(302.63,409.38);
bunny.lineTo(300.13,406.63);
bunny.lineTo(300.13,406.63);
bunny.lineTo(300.13,406.63);
bunny.lineTo(298.00,402.88);
bunny.lineTo(298.00,402.88);
bunny.lineTo(298.00,402.88);
bunny.lineTo(296.63,400.50);
bunny.lineTo(296.63,400.50);
bunny.lineTo(296.63,400.50);
bunny.lineTo(295.38,397.88);
bunny.lineTo(295.38,397.88);
bunny.lineTo(295.38,397.88);
bunny.lineTo(294.50,395.63);
bunny.lineTo(294.50,395.63);
bunny.lineTo(294.50,395.63);
bunny.lineTo(294.13,392.88);
bunny.lineTo(294.13,392.88);
bunny.lineTo(294.13,392.88);
bunny.lineTo(293.13,389.88);
bunny.lineTo(293.13,389.88);
bunny.lineTo(293.13,389.88);
bunny.lineTo(292.38,385.75);
bunny.lineTo(292.38,385.75);
bunny.lineTo(292.38,385.75);
bunny.lineTo(291.75,381.13);
bunny.lineTo(291.75,381.13);
bunny.lineTo(291.75,381.13);
bunny.lineTo(290.88,371.75);
bunny.lineTo(290.88,371.75);
bunny.lineTo(290.88,371.75);
bunny.lineTo(291.13,366.88);
bunny.lineTo(291.13,366.88);
bunny.lineTo(291.13,366.88);
bunny.lineTo(293.25,362.50);
bunny.lineTo(293.25,362.50);
bunny.lineTo(293.25,362.50);
bunny.lineTo(295.38,358.88);
bunny.lineTo(295.38,358.88);
bunny.lineTo(295.38,358.88);
bunny.lineTo(296.75,356.38);
bunny.lineTo(296.75,356.38);
bunny.lineTo(296.75,356.38);
bunny.lineTo(300.00,350.00);
bunny.lineTo(300.00,350.00);
bunny.lineTo(300.00,350.00);
bunny.lineTo(305.63,342.75);
bunny.lineTo(305.63,342.75);
bunny.lineTo(305.63,342.75);
bunny.lineTo(309.88,337.00);
bunny.lineTo(309.88,337.00);
bunny.lineTo(309.88,337.00);
bunny.lineTo(313.50,333.13);
bunny.lineTo(313.50,333.13);
bunny.lineTo(313.50,333.13);
bunny.lineTo(319.25,327.63);
bunny.lineTo(319.25,327.63);
bunny.lineTo(319.25,327.63);
bunny.lineTo(323.38,324.00);
bunny.lineTo(323.38,324.00);
bunny.lineTo(323.38,324.00);
bunny.lineTo(328.38,321.25);
bunny.lineTo(328.38,321.25);
bunny.lineTo(328.38,321.25);
bunny.lineTo(333.75,318.88);
bunny.lineTo(333.75,318.88);
bunny.lineTo(333.75,318.88);
bunny.lineTo(338.50,316.38);
bunny.lineTo(338.50,316.38);
bunny.lineTo(338.50,316.38);
bunny.lineTo(341.63,315.50);
bunny.lineTo(341.63,315.50);
bunny.lineTo(341.63,315.50);
bunny.lineTo(348.50,313.25);
bunny.lineTo(348.50,313.25);
bunny.lineTo(348.50,313.25);
bunny.lineTo(355.25,312.00);
bunny.lineTo(355.25,312.00);
bunny.lineTo(355.25,312.00);
bunny.lineTo(361.00,310.88);
bunny.lineTo(361.00,310.88);
bunny.lineTo(361.00,310.88);
bunny.lineTo(364.50,309.75);
bunny.lineTo(364.50,309.75);
bunny.lineTo(364.50,309.75);
bunny.lineTo(367.75,307.50);
bunny.lineTo(367.75,307.50);
bunny.lineTo(367.75,307.50);
bunny.lineTo(373.25,302.50);
bunny.lineTo(373.25,302.50);
bunny.lineTo(373.25,302.50);
bunny.lineTo(379.13,296.13);
bunny.lineTo(379.13,296.13);
bunny.lineTo(379.13,296.13);
bunny.lineTo(384.25,290.13);
bunny.lineTo(384.25,290.13);
bunny.lineTo(384.25,290.13);
bunny.lineTo(389.25,283.88);
bunny.lineTo(389.25,283.88);
bunny.lineTo(389.25,283.88);
bunny.lineTo(393.25,279.50);
bunny.lineTo(393.25,279.50);
bunny.lineTo(393.25,279.50);
bunny.lineTo(396.63,275.50);
bunny.lineTo(396.63,275.50);
bunny.lineTo(396.63,275.50);
bunny.lineTo(400.50,271.38);
bunny.lineTo(400.50,271.38);
bunny.lineTo(400.50,271.38);
bunny.lineTo(406.25,266.75);
bunny.lineTo(406.25,266.75);
bunny.lineTo(406.25,266.75);
bunny.lineTo(411.50,262.13);
bunny.lineTo(411.50,262.13);
bunny.lineTo(411.50,262.13);
bunny.lineTo(414.88,259.75);
bunny.lineTo(414.88,259.75);
bunny.lineTo(414.88,259.75);
bunny.lineTo(420.25,256.63);
bunny.lineTo(420.25,256.63);
bunny.lineTo(420.25,256.63);
bunny.lineTo(424.63,253.88);
bunny.lineTo(424.63,253.88);
bunny.lineTo(424.63,253.88);
bunny.lineTo(427.50,252.88);
bunny.lineTo(427.50,252.88);
bunny.lineTo(427.50,252.88);
bunny.lineTo(431.13,251.50);
bunny.lineTo(431.13,251.50);
bunny.lineTo(431.13,251.50);
bunny.lineTo(434.25,251.13);
bunny.lineTo(434.25,251.13);
bunny.lineTo(434.25,251.13);
bunny.lineTo(437.63,251.00);
bunny.lineTo(437.63,251.00);
bunny.lineTo(437.63,251.00);
bunny.lineTo(442.50,252.25);
bunny.lineTo(442.50,252.25);
bunny.lineTo(442.50,252.25);
bunny.lineTo(445.50,253.75);
bunny.lineTo(445.50,253.75);
bunny.lineTo(445.50,253.75);
bunny.lineTo(447.88,255.25);
bunny.lineTo(447.88,255.25);
bunny.lineTo(447.88,255.25);
bunny.lineTo(450.50,258.25);
bunny.lineTo(450.50,258.25);
bunny.lineTo(450.50,258.25);
bunny.lineTo(451.88,260.75);
bunny.lineTo(451.88,260.75);
bunny.lineTo(451.88,260.75);
bunny.lineTo(453.38,264.13);
bunny.lineTo(453.38,264.13);
bunny.lineTo(453.38,264.13);
bunny.lineTo(454.25,266.88);
bunny.lineTo(454.25,266.88);
bunny.lineTo(454.25,266.88);
bunny.lineTo(455.50,271.13);
bunny.lineTo(455.50,271.13);
bunny.lineTo(455.50,271.13);
bunny.lineTo(455.63,274.75);
bunny.lineTo(455.63,274.75);
bunny.lineTo(455.63,274.75);
bunny.lineTo(456.75,276.50);
bunny.lineTo(456.75,276.50);
bunny.lineTo(456.75,276.50);
bunny.lineTo(458.63,278.00);
bunny.lineTo(458.63,278.00);
bunny.lineTo(458.63,278.00);
bunny.lineTo(460.00,278.88);
bunny.lineTo(460.00,278.88);
bunny.lineTo(460.00,278.88);
bunny.lineTo(461.00,280.75);
bunny.lineTo(461.00,280.75);
bunny.lineTo(461.00,280.75);
bunny.lineTo(462.75,281.00);
bunny.lineTo(462.75,281.00);
bunny.lineTo(462.75,281.00);
bunny.lineTo(464.88,281.88);
bunny.lineTo(464.88,281.88);
bunny.lineTo(464.88,281.88);
bunny.lineTo(467.38,283.00);
bunny.lineTo(467.38,283.00);
bunny.lineTo(467.38,283.00);
bunny.lineTo(470.13,284.50);
bunny.lineTo(470.13,284.50);
bunny.lineTo(470.13,284.50);
bunny.lineTo(472.00,286.13);
bunny.lineTo(472.00,286.13);
bunny.lineTo(473.63,287.50);
bunny.lineTo(477.13,290.13);
bunny.lineTo(477.13,290.13);
bunny.lineTo(477.13,290.13);
bunny.lineTo(479.50,292.25);
bunny.lineTo(479.50,292.25);
bunny.lineTo(479.50,292.25);
bunny.lineTo(481.50,295.00);
bunny.lineTo(481.50,295.00);
bunny.lineTo(481.50,295.00);
bunny.lineTo(483.38,299.13);
bunny.lineTo(483.38,299.13);
bunny.lineTo(483.38,299.13);
bunny.lineTo(483.38,303.63);
bunny.lineTo(483.38,303.63);
bunny.lineTo(483.38,303.63);
bunny.lineTo(482.63,308.00);
bunny.lineTo(482.63,308.00);
bunny.lineTo(482.63,308.00);
bunny.lineTo(480.75,311.75);
bunny.lineTo(480.75,311.75);
bunny.lineTo(480.75,311.75);
bunny.lineTo(469.25,325.00);
bunny.lineTo(469.25,325.00);
bunny.lineTo(469.25,325.00);
bunny.lineTo(464.00,330.63);
bunny.lineTo(464.00,330.63);
bunny.lineTo(464.00,330.63);
bunny.lineTo(458.50,337.13);
bunny.lineTo(458.50,337.13);
bunny.lineTo(458.50,337.13);
bunny.lineTo(451.75,345.13);
bunny.lineTo(451.75,345.13);
bunny.lineTo(451.75,345.13);
bunny.lineTo(448.38,349.63);
bunny.lineTo(448.38,349.63);
bunny.lineTo(448.38,349.63);
bunny.lineTo(446.00,353.13);
bunny.lineTo(446.00,353.13);
bunny.lineTo(446.00,353.13);
bunny.lineTo(443.38,357.75);
bunny.lineTo(443.38,357.75);
bunny.lineTo(443.38,357.75);
bunny.lineTo(442.13,361.13);
bunny.lineTo(442.13,361.13);
bunny.lineTo(442.13,361.13);
bunny.lineTo(441.88,367.38);
bunny.lineTo(441.88,367.38);
bunny.lineTo(441.88,367.38);
bunny.lineTo(441.75,371.75);
bunny.lineTo(441.75,371.75);
bunny.lineTo(441.75,371.75);
bunny.lineTo(442.13,375.63);
bunny.lineTo(442.13,375.63);
bunny.lineTo(442.13,375.63);
bunny.lineTo(443.00,381.88);
bunny.lineTo(443.00,381.88);
bunny.lineTo(443.00,381.88);
bunny.lineTo(444.38,385.75);
bunny.lineTo(444.38,385.75);
bunny.lineTo(444.38,385.75);
bunny.lineTo(446.50,390.75);
bunny.lineTo(446.50,390.75);
bunny.lineTo(446.50,390.75);
bunny.lineTo(450.00,396.38);
bunny.lineTo(450.00,396.38);
bunny.lineTo(450.00,396.38);
bunny.lineTo(452.88,399.50);
bunny.lineTo(452.88,399.50);
bunny.lineTo(452.88,399.50);
bunny.lineTo(457.25,404.25);
bunny.lineTo(457.25,404.25);
bunny.lineTo(457.25,404.25);
bunny.lineTo(462.38,409.50);
bunny.lineTo(462.38,409.50);
bunny.lineTo(462.38,409.50);
bunny.lineTo(467.25,414.50);
bunny.lineTo(467.25,414.50);
bunny.lineTo(467.25,414.50);
bunny.lineTo(471.25,418.63);
bunny.lineTo(471.25,418.63);
bunny.lineTo(471.25,418.63);
bunny.lineTo(473.25,421.38);
bunny.lineTo(473.25,421.38);
bunny.lineTo(473.25,421.38);
bunny.lineTo(476.63,426.13);
bunny.lineTo(476.63,426.13);
bunny.lineTo(476.63,426.13);
bunny.lineTo(480.75,431.13);
bunny.lineTo(480.75,431.13);
bunny.lineTo(480.75,431.13);
bunny.lineTo(483.50,435.13);
bunny.lineTo(483.50,435.13);
bunny.lineTo(483.50,435.13);
bunny.lineTo(485.25,439.63);
bunny.lineTo(485.25,439.63);
bunny.lineTo(485.25,439.63);
bunny.lineTo(487.50,444.75);
bunny.lineTo(487.50,444.75);
bunny.lineTo(487.50,444.75);
bunny.lineTo(489.13,448.88);
bunny.lineTo(489.13,448.88);
bunny.lineTo(489.13,448.88);
bunny.lineTo(489.75,453.88);
bunny.lineTo(489.75,453.88);
bunny.lineTo(489.75,453.88);
bunny.lineTo(490.63,459.38);
bunny.lineTo(490.63,459.38);
bunny.lineTo(490.63,459.38);
bunny.lineTo(492.38,466.25);
bunny.lineTo(492.38,466.25);
bunny.lineTo(492.38,466.25);
bunny.lineTo(493.38,472.88);
bunny.lineTo(493.38,472.88);
bunny.lineTo(493.38,472.88);
bunny.lineTo(493.88,477.13);
bunny.lineTo(493.88,477.13);
bunny.lineTo(493.88,477.13);
bunny.lineTo(494.38,480.63);
bunny.lineTo(494.38,480.63);
bunny.lineTo(494.38,480.63);
bunny.lineTo(495.50,483.38);
bunny.lineTo(495.50,483.38);
bunny.lineTo(495.50,483.38);
bunny.lineTo(498.00,485.25);
bunny.lineTo(498.00,485.25);
bunny.lineTo(498.00,485.25);
bunny.lineTo(499.50,488.00);
bunny.lineTo(499.50,488.00);
bunny.lineTo(499.50,488.00);
bunny.lineTo(500.63,491.50);
bunny.lineTo(500.63,491.50);
bunny.lineTo(500.63,491.50);
bunny.lineTo(501.25,497.13);
bunny.lineTo(501.25,497.13);
bunny.lineTo(501.25,497.13);
bunny.lineTo(500.13,501.13);
bunny.lineTo(500.13,501.13);
bunny.lineTo(500.13,501.13);
bunny.lineTo(499.13,505.63);
bunny.lineTo(499.13,505.63);
bunny.lineTo(499.13,505.63);
bunny.lineTo(497.88,507.88);
bunny.lineTo(497.88,507.88);
bunny.lineTo(497.88,507.88);
bunny.lineTo(495.88,512.25);
bunny.lineTo(495.88,512.25);
bunny.lineTo(495.88,512.25);
bunny.lineTo(494.38,516.25);
bunny.lineTo(494.38,516.25);
bunny.lineTo(494.38,516.25);
bunny.lineTo(493.25,518.63);
bunny.lineTo(493.25,518.63);
bunny.lineTo(493.25,518.63);
bunny.lineTo(491.88,521.88);
bunny.lineTo(491.88,521.88);
bunny.lineTo(491.88,521.88);
bunny.lineTo(489.38,524.00);
bunny.lineTo(489.38,524.00);
bunny.lineTo(489.38,524.00);
bunny.lineTo(489.13,526.63);
bunny.lineTo(489.13,526.63);
bunny.lineTo(489.13,526.63);
bunny.lineTo(488.13,528.75);
bunny.lineTo(488.13,528.75);
bunny.closePath();
g2.draw(bunny);
Color gold = new Color(255,215,0);
g2.setColor(gold);
g2.fill(bunny);
Path2D chocoears = new Path2D.Float();
chocoears.moveTo(473.63, 287.50);
chocoears.lineTo(473.63,287.50);
chocoears.lineTo(477.13,290.13);
chocoears.lineTo(477.13,290.13);
chocoears.lineTo(477.13,290.13);
chocoears.lineTo(479.50,292.25);
chocoears.lineTo(479.50,292.25);
chocoears.lineTo(479.50,292.25);
chocoears.lineTo(481.50,295.00);
chocoears.lineTo(481.50,295.00);
chocoears.lineTo(481.50,295.00);
chocoears.lineTo(483.38,299.13);
chocoears.lineTo(483.38,299.13);
chocoears.lineTo(483.38,299.13);
chocoears.lineTo(483.38,303.63);
chocoears.lineTo(483.38,303.63);
chocoears.lineTo(483.38,303.63);
chocoears.lineTo(482.63,308.00);
chocoears.lineTo(482.63,308.00);
chocoears.lineTo(482.63,308.00);
chocoears.lineTo(480.75,311.75);
chocoears.lineTo(480.75,311.75);
chocoears.lineTo(480.75,311.75);
chocoears.lineTo(469.25,325.00);
chocoears.lineTo(469.25,325.00);
chocoears.lineTo(469.25,325.00);
chocoears.lineTo(464.00,330.63);
chocoears.lineTo(464.00,330.63);
chocoears.lineTo(464.00,330.63);
chocoears.lineTo(458.50,337.13);
chocoears.lineTo(458.50,337.13);
chocoears.lineTo(458.50,337.13);
chocoears.lineTo(451.75,345.13);
chocoears.lineTo(451.75,345.13);
chocoears.lineTo(451.75,345.13);
chocoears.lineTo(442.33,351.00);
chocoears.lineTo(442.33,351.00);
chocoears.lineTo(442.33,351.00);
chocoears.lineTo(435.33,351.00);
chocoears.lineTo(435.33,351.00);
chocoears.lineTo(435.33,351.00);
chocoears.lineTo(432.00,349.00);
chocoears.lineTo(432.00,349.00);
chocoears.lineTo(432.00,349.00);
chocoears.lineTo(427.00,348.67);
chocoears.lineTo(427.00,348.67);
chocoears.lineTo(427.00,348.67);
chocoears.lineTo(420.00,342.67);
chocoears.lineTo(420.00,342.67);
chocoears.lineTo(420.00,342.67);
chocoears.lineTo(415.33,339.33);
chocoears.lineTo(415.33,339.33);
chocoears.lineTo(415.33,339.33);
chocoears.lineTo(412.00,341.00);
chocoears.lineTo(412.00,341.00);
chocoears.lineTo(412.00,341.00);
chocoears.lineTo(408.33,337.00);
chocoears.lineTo(408.33,337.00);
chocoears.lineTo(408.33,337.00);
chocoears.lineTo(407.33,333.33);
chocoears.lineTo(407.33,333.33);
chocoears.lineTo(407.33,333.33);
chocoears.lineTo(403.67,329.67);
chocoears.lineTo(403.67,329.67);
chocoears.lineTo(403.67,329.67);
chocoears.lineTo(399.00,331.67);
chocoears.lineTo(399.00,331.67);
chocoears.lineTo(399.00,331.67);
chocoears.lineTo(396.33,329.33);
chocoears.lineTo(396.33,329.33);
chocoears.lineTo(396.33,329.33);
chocoears.lineTo(394.33,326.67);
chocoears.lineTo(394.33,326.67);
chocoears.lineTo(394.33,326.67);
chocoears.lineTo(386.67,326.33);
chocoears.lineTo(386.67,326.33);
chocoears.lineTo(386.67,326.33);
chocoears.lineTo(382.33,320.67);
chocoears.lineTo(382.33,320.67);
chocoears.lineTo(382.33,320.67);
chocoears.lineTo(378.00,322.00);
chocoears.lineTo(378.00,322.00);
chocoears.lineTo(378.00,322.00);
chocoears.lineTo(372.00,320.00);
chocoears.lineTo(372.00,320.00);
chocoears.lineTo(372.00,320.00);
chocoears.lineTo(373.33,318.00);
chocoears.lineTo(373.33,318.00);
chocoears.lineTo(373.33,318.00);
chocoears.lineTo(371.33,315.67);
chocoears.lineTo(371.33,315.67);
chocoears.lineTo(371.33,315.67);
chocoears.lineTo(365.33,316.00);
chocoears.lineTo(365.33,316.00);
chocoears.lineTo(365.33,316.00);
chocoears.lineTo(355.25,312.00);
chocoears.lineTo(355.25,312.00);
chocoears.lineTo(355.25,312.00);
chocoears.lineTo(361.00,310.88);
chocoears.lineTo(361.00,310.88);
chocoears.lineTo(361.00,310.88);
chocoears.lineTo(364.50,309.75);
chocoears.lineTo(364.50,309.75);
chocoears.lineTo(364.50,309.75);
chocoears.lineTo(367.75,307.50);
chocoears.lineTo(367.75,307.50);
chocoears.lineTo(367.75,307.50);
chocoears.lineTo(373.25,302.50);
chocoears.lineTo(373.25,302.50);
chocoears.lineTo(373.25,302.50);
chocoears.lineTo(379.13,296.13);
chocoears.lineTo(379.13,296.13);
chocoears.lineTo(379.13,296.13);
chocoears.lineTo(384.25,290.13);
chocoears.lineTo(384.25,290.13);
chocoears.lineTo(384.25,290.13);
chocoears.lineTo(389.25,283.88);
chocoears.lineTo(389.25,283.88);
chocoears.lineTo(389.25,283.88);
chocoears.lineTo(393.25,279.50);
chocoears.lineTo(393.25,279.50);
chocoears.lineTo(393.25,279.50);
chocoears.lineTo(396.63,275.50);
chocoears.lineTo(396.63,275.50);
chocoears.lineTo(396.63,275.50);
chocoears.lineTo(400.50,271.38);
chocoears.lineTo(400.50,271.38);
chocoears.lineTo(400.50,271.38);
chocoears.lineTo(406.25,266.75);
chocoears.lineTo(406.25,266.75);
chocoears.lineTo(406.25,266.75);
chocoears.lineTo(411.50,262.13);
chocoears.lineTo(411.50,262.13);
chocoears.lineTo(411.50,262.13);
chocoears.lineTo(414.88,259.75);
chocoears.lineTo(414.88,259.75);
chocoears.lineTo(414.88,259.75);
chocoears.lineTo(420.25,256.63);
chocoears.lineTo(420.25,256.63);
chocoears.lineTo(420.25,256.63);
chocoears.lineTo(424.63,253.88);
chocoears.lineTo(424.63,253.88);
chocoears.lineTo(424.63,253.88);
chocoears.lineTo(427.50,252.88);
chocoears.lineTo(427.50,252.88);
chocoears.lineTo(427.50,252.88);
chocoears.lineTo(431.13,251.50);
chocoears.lineTo(431.13,251.50);
chocoears.lineTo(431.13,251.50);
chocoears.lineTo(434.25,251.13);
chocoears.lineTo(434.25,251.13);
chocoears.lineTo(434.25,251.13);
chocoears.lineTo(437.63,251.00);
chocoears.lineTo(437.63,251.00);
chocoears.lineTo(437.63,251.00);
chocoears.lineTo(442.50,252.25);
chocoears.lineTo(442.50,252.25);
chocoears.lineTo(442.50,252.25);
chocoears.lineTo(445.50,253.75);
chocoears.lineTo(445.50,253.75);
chocoears.lineTo(445.50,253.75);
chocoears.lineTo(447.88,255.25);
chocoears.lineTo(447.88,255.25);
chocoears.lineTo(447.88,255.25);
chocoears.lineTo(450.50,258.25);
chocoears.lineTo(450.50,258.25);
chocoears.lineTo(450.50,258.25);
chocoears.lineTo(451.88,260.75);
chocoears.lineTo(451.88,260.75);
chocoears.lineTo(451.88,260.75);
chocoears.lineTo(453.38,264.13);
chocoears.lineTo(453.38,264.13);
chocoears.lineTo(453.38,264.13);
chocoears.lineTo(454.25,266.88);
chocoears.lineTo(454.25,266.88);
chocoears.lineTo(454.25,266.88);
chocoears.lineTo(455.50,271.13);
chocoears.lineTo(455.50,271.13);
chocoears.lineTo(455.50,271.13);
chocoears.lineTo(455.63,274.75);
chocoears.lineTo(455.63,274.75);
chocoears.lineTo(455.63,274.75);
chocoears.lineTo(456.75,276.50);
chocoears.lineTo(456.75,276.50);
chocoears.lineTo(456.75,276.50);
chocoears.lineTo(458.63,278.00);
chocoears.lineTo(458.63,278.00);
chocoears.lineTo(458.63,278.00);
chocoears.lineTo(460.00,278.88);
chocoears.lineTo(460.00,278.88);
chocoears.lineTo(460.00,278.88);
chocoears.lineTo(461.00,280.75);
chocoears.lineTo(461.00,280.75);
chocoears.lineTo(461.00,280.75);
chocoears.lineTo(462.75,281.00);
chocoears.lineTo(462.75,281.00);
chocoears.lineTo(462.75,281.00);
chocoears.lineTo(464.88,281.88);
chocoears.lineTo(464.88,281.88);
chocoears.lineTo(464.88,281.88);
chocoears.lineTo(467.38,283.00);
chocoears.lineTo(467.38,283.00);
chocoears.lineTo(467.38,283.00);
chocoears.lineTo(470.13,284.50);
chocoears.lineTo(470.13,284.50);
chocoears.lineTo(470.13,284.50);
chocoears.lineTo(472.00,286.13);
chocoears.lineTo(472.00,286.13);
chocoears.closePath();
g2.draw(chocoears);
Color milkChocolate = new Color(111,68,51);
g2.setColor(milkChocolate);
g2.fill(chocoears);
//Grass
Rectangle grass = new Rectangle(0,525,getWidth(),100);
Color lawnGreen = new Color(124,252,0);
g2.setColor(lawnGreen);
g2.fill(grass);
List<Arc2D> blades = new ArrayList<Arc2D>();
for (int x = 0; x < getWidth(); x += 10) {
blades.add(new Arc2D.Double(x, 500, 10, 35, 105, 180, Arc2D.OPEN));
blades.add(new Arc2D.Double(x - 5, 510, 10, 35, 105, 180, Arc2D.OPEN));
blades.add(new Arc2D.Double(x, 520, 10, 35, 105, 180, Arc2D.OPEN));
}
Color yellowGreen = new Color(107, 142, 35);
g2.setColor(yellowGreen);
for (Shape blade : blades) {
g2.draw(blade);
}
Ellipse2D.Double circle = new Ellipse2D.Double(60,100,25,25);
g2.setColor(Color.RED);
g2.fill(circle);
Rectangle box = new Rectangle(150,100,20,80);
g2.setColor(Color.YELLOW);
g2.fill(box);
}
}
Whatever you do I would
create the Path2D objects outside of the paintComponent() method.
add each Path2D object to an ArrayList
in the paintComponent() method you can iterate through the ArrayList to paint each Path2D
Or another approach is to just have a method that:
creates each Path2D object when the class is created
then you paint the Path2D object to a BufferedImage
now that you have a BufferedImage you can create an ImageIcon and Just use a JLabel in you app and you don't need the custom panel.
Heres 2 options I would consider for cleaning up the pathing.
Use SVG, one library I know is Batik found at: http://xmlgraphics.apache.org/batik/
Once I did a project where the paths I needed would be created in photoshop then exported to Illustrator. The file format was each action was line delineated, each line was space delineated and consisted of a set of points with the last entry the command name. Here was a sample input.
10 10 m
15 15 l
10 10 12 12 v
As you can see you just read the file, create your path and do an if test for the last letter on the line and use the appropriate command with for the points. Of course you can make up your own format or procedures but it sure was nice using photoshop for me :)