Detect mishapen blobs in python using OpenCV - python

I would like to detect two blobs in the following image:
Original:
I want to have the inside detected like this:
I also want the outside circle detected:
But I'm applying OpenCV's simple blob detection right now and it is not giving me the desired results. This is my code:
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector()
# Detect blobs.
keypoints = detector.detect(image)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(image, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
final = Image.fromarray(im_with_keypoints)
final.show()
But this is what the blob detector detects:
Hugh Circle detection in OpenCV also doesn't correctly identify the two shapes.
Update:
I've also tried ellipse fitting, but instead of detecting either of the blobs, it detects some random line in the image. Here is the code I used for ellipse fitting.
ret,thresh = cv2.threshold(image,127,255,0)
contours,hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)
print M
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(im2,ellipse,(0,255,0),2)
final = Image.fromarray(image)
final.show()
Any help in detecting these blobs is appreciated.

For detecting the inner blob, you can also try clustering and MSERs, because the region looks flat. I downsampled a cropped version of your image and applied these techniques.
Downsampled image
Here I use kmeans with 10 clusters. The drawback is you have to specify the number of clusters.
Here I use MSER. It is more robust.
The code is in c++. Note that you have to scale the outputs to see the details.
Mat im = imread("2L6hP.png", 0);
Mat dw;
pyrDown(im, dw);
// kmeans with 10 clusters
int k = 10;
Mat rgb32fc, lbl;
dw.convertTo(rgb32fc, CV_32F);
int imsize[] = {rgb32fc.rows, rgb32fc.cols};
Mat color = rgb32fc.reshape(1, rgb32fc.rows*rgb32fc.cols);
kmeans(color, k, lbl, TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0), 2, KMEANS_PP_CENTERS);
Mat lbl2d = lbl.reshape(1, 2, imsize);
Mat lbldisp; // clustered result
lbl2d.convertTo(lbldisp, CV_8U, 1);
// MSER
MSER mser;
vector<vector<Point>> regions;
mser(dw, regions);
Mat regionsMat = Mat::zeros(dw.rows, dw.cols, CV_8U); // MSER result
for (size_t i = 0; i < regions.size(); i++)
{
for (Point pt: regions[i])
{
uchar& val = regionsMat.at<uchar>(pt);
if (val > 0)
{
val += 1;
}
else
{
val = 1;
}
}
}

Related

OpenCV HoughCircles parameters for detecting circles (microstructure spheres)

I am creating program that helps processing microstructure images. One of the function is detecting circles with the same radius. User draws one circle, my program spots others. I've already implemented distance transform method
I am trying to create method that uses HoughCircles. However, I am confused with its parameters.
My code:
def find_circles_with_radius_haugh(path, radius):
img = cv2.imread(path)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(img_gray, cv2.HOUGH_GRADIENT, int(radius),
1.5,
param1=80, param2=40,
minRadius=int(radius * 0.9),
maxRadius=int(radius * 1.1))
res = list()
if circles is not None:
for i in circles[0, :]:
res.append((i[0], i[1], i[2]))
return res
Original picture:
My result of detecting circles with radius 57 pixels (+- 10%):
Please help me with better processing images like that.
I might try findContours method, but I don't know any filters that will make borders on this picture clearer.
I tried a little.
My idea is simply using filter2D instead of Hough-Transform.
Because detection target is the circles has specific radius, if edge of circles detected clearly, the center of the circles will be able to found by convoluting circular mask to the edge image.
I checked the filter2D(=convolution) result with following code (C++).
int main()
{
//This source image "MicroSpheres.png" was copied from this question
cv::Mat Src = cv::imread( "MicroSpheres.png", cv::IMREAD_GRAYSCALE );
if( Src.empty() )return 0;
//Test with 50% Scale
cv::resize( Src, Src, cv::Size(0,0), 0.5, 0.5, cv::INTER_AREA );
cv::imshow( "Src", Src );
const int Radius = cvRound(57 * 0.5); //So, Radius is also 50% scale
//Trying to detect edge of circles
cv::Mat EdgeImg;
{
cv::Mat Test;
cv::medianBlur( Src, Test, 5 );
cv::morphologyEx( Test, Test, cv::MORPH_GRADIENT, cv::Mat() );
cv::imshow( "Test", Test );
cv::adaptiveThreshold( Test, EdgeImg, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, (Test.rows/6)|0x01, -6 );
cv::imshow( "EdgeImg", EdgeImg );
}
cv::Mat BufferFor_imwrite = EdgeImg.clone();
//filter2D
cv::Mat FilterResult;
{
const int FilterRadius = Radius + 2;
const int FilterSize = FilterRadius*2 + 1;
cv::Mat Filter = cv::Mat::zeros( FilterSize,FilterSize, CV_32F );
cv::circle( Filter, cv::Point(FilterRadius,FilterRadius), Radius/2, cv::Scalar(-1), -1 );
cv::circle( Filter, cv::Point(FilterRadius,FilterRadius), Radius, cv::Scalar(1), 3 );
cv::filter2D( EdgeImg, FilterResult, CV_32F, Filter );
}
{//Very lazy check of the filter2D result.
double Min, Max;
cv::minMaxLoc( FilterResult, &Min, &Max );
double scale = 255 / (Max-Min);
cv::Mat Show;
FilterResult.convertTo( Show, CV_8U, scale, -Min*scale );
cv::imshow( "Filter2D_Result", Show );
cv::vconcat( BufferFor_imwrite, Show, BufferFor_imwrite );
//(Estimating center of circles based onthe filter2D result.)
// Here, just only simple thresholding is implemented.
// At least non-maximum suppression must be done, I think.
cv::Mat Centers;
cv::threshold( FilterResult, Centers, (Max+Min)*0.6, 255, cv::THRESH_BINARY );
Centers.convertTo( Centers, CV_8U );
Show = Src * 0.5;
Show.setTo( cv::Scalar(255), Centers );
cv::imshow( "Centers", Show );
cv::vconcat( BufferFor_imwrite, Show, BufferFor_imwrite );
}
if( cv::waitKey() == 's' ){ cv::imwrite( "Result.png", BufferFor_imwrite ); }
return 0;
}
The following image is result. 3 images are concatenated vertically.
edge detection result
filter2D result
Circle center estimation result (very lazy. just binarized the filter2D result and overlapped it onto source image.)
I can't say this is perfect, but it looks like that the result roughly indicates some centers.
Rewrote #fana code in Python
import cv2
import numpy as np
img = cv2.imread('spheres1.bmp')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (0, 0), gray, 0.5, 0.5, cv2.INTER_AREA)
cv2.imwrite("resized.png", gray)
radius = round(57 * 0.5)
test = cv2.medianBlur(gray, 5)
struct_elem = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# might be better to use "I" matrix
# struct_elem = np.ones((3,3), np.uint8)
test = cv2.morphologyEx(test, cv2.MORPH_GRADIENT, kernel=struct_elem)
cv2.imwrite("MorphologyEx.png", test)
edge_img = cv2.adaptiveThreshold(test, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, int(len(test) / 6) | 0x01, -6)
cv2.imwrite("EdgeImg.png", edge_img );
buffer_for_imwrite = edge_img.copy()
filter_radius = radius + 2
filter_size = filter_radius * 2 + 1
img_filter = np.zeros((filter_size, filter_size))
cv2.circle(img_filter, (filter_radius, filter_radius), int(radius / 2), -1, -1)
cv2.circle(img_filter, (filter_radius, filter_radius), radius, 1, 3)
# second circle better to generate with smaller width like this:
# cv2.circle(img_filter, (filter_radius, filter_radius), radius, 1, 2)
cv2.imwrite("Filter.png", img_filter)
filter_result = cv2.filter2D(edge_img, cv2.CV_32F, img_filter)
cv2.imwrite("FilterResult.png", filter_result)
min_val, max_val, _, _ = cv2.minMaxLoc(filter_result)
scale = 255 / (max_val - min_val)
show = np.uint8(filter_result * scale - min_val * scale)
cv2.imwrite("Filter2D_Result.png", show)
_, centers = cv2.threshold(filter_result, (max_val + min_val) * 0.6, 255, cv2.THRESH_BINARY)
centers = np.uint8(centers)
show = gray * 0.5
show[np.where(centers == 255)] = 255
cv2.imwrite("Centers.png", show)

Python Image Processing on Captcha how to remove noise

I am so new on Image Processing and what I'm trying to do is clearing the noise from captchas;
For captchas, I have different types of them:
For the first one what I did is :
Firstly, I converted every pixel that is not black to the black. Then, I found a pattern that is a noise from the image and deleted it. For the first captcha, it was easy to clear it and I found the text with tesseract.
But I am looking for a solution for the second and the third.
How this must go like? I mean what are the possible methods to clear it?
This is how I delete patterns:
def delete(searcher,h2,w2):
h = h2
w = w2
search = searcher
search = search.convert("RGBA")
herear = np.asarray(search)
bigar = np.asarray(imgCropped)
hereary, herearx = herear.shape[:2]
bigary, bigarx = bigar.shape[:2]
stopx = bigarx - herearx + 1
stopy = bigary - hereary + 1
pix = imgCropped.load()
for x in range(0, stopx):
for y in range(0, stopy):
x2 = x + herearx
y2 = y + hereary
pic = bigar[y:y2, x:x2]
test = (pic == herear)
if test.all():
for q in range(h):
for k in range(w):
pix[x+k,y+q] = (255,255,255,255)
Sorry for the variable names, I was just testing function.
Thanks..
This is as far as I can get:
You probably know about medianBlur function which finds the median value in every kernel and substitute that value to kernel's center. We can do something similar to that but instead of the median, use the max value then the min value. With a median bluring too, I got some results. I know they are not perfect but I hope it gives you some ideas ( you can play with the sizes of the input image and the kernels, it may make the results a little better).
I don't have python installed right now, so I share the exact C++ code that I have used:
Mat im1 = imread("E:/1/3.jpg", 0);
Mat im2, im3;
im2 = Mat::zeros(im1.size(), CV_8U);
for (size_t i = 1; i < im1.rows-1; i++)
{
for (size_t j = 1; j < im1.cols-1; j++)
{
double minVal, maxVal = 0;
minMaxIdx(im1(Rect(j - 1, i - 1, 3, 3)), &minVal, &maxVal);
im2.at<uchar>(i, j) = maxVal;
}
}
imshow("(1) max bluring", im2);
medianBlur(im2, im2, 3);
imshow("(2) median bluring", im2);
im2.copyTo(im1);
im2 = Mat::zeros(im1.size(), CV_8U);
for (size_t i = 1; i < im1.rows - 1; i++)
{
for (size_t j = 1; j < im1.cols - 1; j++)
{
double minVal, maxVal = 0;
minMaxIdx(im1(Rect(j - 1, i - 1, 3, 3)), &minVal, &maxVal);
im2.at<uchar>(i, j) = minVal;
}
}
imshow("(3) min bluring", im2);
Mat tmp;
double st = threshold(im2, tmp, 10, 255, THRESH_OTSU);
threshold(im2, im2, st + 14, 255, THRESH_BINARY_INV);
//dilate(im2, im2, Mat::ones(3, 3, CV_8U));
imshow("(4) final", im2);
waitKey(0);
By the way in such cases, deep Learning methods like YOLO and RCNN are the best methods. Try them too.
Here is my solution,
Firstly I got the background pattern(Edited on paint by hand). From:
After that, I created a blank image to fill it with differences between the pattern and image.
img = Image.open("x.png").convert("RGBA")
pattern = Image.open("y.png").convert("RGBA")
pixels = img.load()
pixelsPattern = pattern.load()
new = Image.new("RGBA", (150, 50))
pixelNew = new.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
if(pixels[i,j] != pixelsPattern[i,j]):
pixelNew[i,j] = pixels[i,j]
new.save("differences.png")
Here are the differences..
and finally, I added blur and cleared the bits which are not black.
Result :
With pytesseract result is 2041, it is wrong for this image but the general rate is around %60.
You can use opencv library for image processing. Very usefull could be this opencv documentation page. Then try to extract your number through findCountour method like:
import cv2
import numpy as np
image = cv2.imread('C:\\E0snN.png')
cv2.waitKey(0)
# Grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.bitwise_not(gray)
# Threshold
ret,thresh = cv2.threshold(gray,150,255,1)
# Get countours
contours,h = cv2.findContours(thresh,1,2)
# Draw
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
After that there is following result:
It's far from perfect but if you try with different threshold values e.g. :
ret,thresh = cv2.threshold(gray,127,255,1)
you can get better results.

Opencv: Jetmap or colormap to grayscale, reverse applyColorMap()

To convert to colormap, I do
import cv2
im = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)
im_color = cv2.applyColorMap(im, cv2.COLORMAP_JET)
cv2.imwrite('colormap.jpg', im_color)
Then,
cv2.imread('colormap.jpg')
# ??? What should I do here?
Obviously, reading it in grayscale (with , 0) wouldn't magically give us the grayscale, so how do I do it?
You could create an inverse of the colormap, i.e., a lookup table from the colormap values to the associated gray values. If using a lookup table, exact values of the original colormap are needed. In that case, the false color images will most likely need to be saved in a lossless format to avoid colors being changed. There's probably a faster way to do map over the numpy array. If exact values cannot be preserved, then a nearest neighbor lookup in the inverse map would be needed.
import cv2
import numpy as np
# load a color image as grayscale, convert it to false color, and save false color version
im_gray = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('gray_image_original.png', im_gray)
im_color = cv2.applyColorMap(im_gray, cv2.COLORMAP_JET)
cv2.imwrite('colormap.png', im_color) # save in lossless format to avoid colors changing
# create an inverse from the colormap to gray values
gray_values = np.arange(256, dtype=np.uint8)
color_values = map(tuple, cv2.applyColorMap(gray_values, cv2.COLORMAP_JET).reshape(256, 3))
color_to_gray_map = dict(zip(color_values, gray_values))
# load false color and reserve space for grayscale image
false_color_image = cv2.imread('colormap.png')
# apply the inverse map to the false color image to reconstruct the grayscale image
gray_image = np.apply_along_axis(lambda bgr: color_to_gray_map[tuple(bgr)], 2, false_color_image)
# save reconstructed grayscale image
cv2.imwrite('gray_image_reconstructed.png', gray_image)
# compare reconstructed and original gray images for differences
print('Number of pixels different:', np.sum(np.abs(im_gray - gray_image) > 0))
The other answer works if you have exact color values.
If your colors have been compressed lossily (JPEG), you need a different approach.
Here's an approach using FLANN. It finds the nearest color and tells you the difference too, so you can handle implausible values.
complete notebook: https://gist.github.com/crackwitz/ccd54145bec1297ccdd4a0c8f4971deb
Highlights:
norm = cv.NORM_L2
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
fm = cv.FlannBasedMatcher(index_params, search_params)
# JET, BGR order, excluding special palette values (>= 256)
fm.add(255 * np.float32([jet._lut[:256, (2,1,0)]])) # jet
fm.train()
# look up all pixels
query = im.reshape((-1, 3)).astype(np.float32)
matches = fm.match(query)
# statistics: `result` is palette indices ("grayscale image")
output = np.uint16([m.trainIdx for m in matches]).reshape(height, width)
result = np.where(output < 256, output, 0).astype(np.uint8)
dist = np.uint8([m.distance for m in matches]).reshape(height, width)
Source of colormapped picture: Separating Object Contours OpenCV
Have been facing a similar problem while working with a served JPEG compressed image. Since I am on c++, resorting to matplotlib is not an option.
The alternative is to fetch one of the lookup tables (lut) corresponding to the desired colormap, e.g. "jet", available in the imgproc/src/colormap.cpp source file. Unfortunately, what could be easily retrieved using cv::colormap::Jet(n) (where 'n' would even allow to interpolate more points) is not accessible through OpenCV's API.
That said, here is my solution based on #Christoph Rackwitz's answer:
// GNU Octave colormap "jet" as in cv::colormap::Jet()._lut
const float r[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00588235294117645f,0.02156862745098032f,0.03725490196078418f,0.05294117647058827f,0.06862745098039214f,0.084313725490196f,0.1000000000000001f,0.115686274509804f,0.1313725490196078f,0.1470588235294117f,0.1627450980392156f,0.1784313725490196f,0.1941176470588235f,0.2098039215686274f,0.2254901960784315f,0.2411764705882353f,0.2568627450980392f,0.2725490196078431f,0.2882352941176469f,0.303921568627451f,0.3196078431372549f,0.3352941176470587f,0.3509803921568628f,0.3666666666666667f,0.3823529411764706f,0.3980392156862744f,0.4137254901960783f,0.4294117647058824f,0.4450980392156862f,0.4607843137254901f,0.4764705882352942f,0.4921568627450981f,0.5078431372549019f,0.5235294117647058f,0.5392156862745097f,0.5549019607843135f,0.5705882352941174f,0.5862745098039217f,0.6019607843137256f,0.6176470588235294f,0.6333333333333333f,0.6490196078431372f,0.664705882352941f,0.6803921568627449f,0.6960784313725492f,0.7117647058823531f,0.7274509803921569f,0.7431372549019608f,0.7588235294117647f,0.7745098039215685f,0.7901960784313724f,0.8058823529411763f,0.8215686274509801f,0.8372549019607844f,0.8529411764705883f,0.8686274509803922f,0.884313725490196f,0.8999999999999999f,0.9156862745098038f,0.9313725490196076f,0.947058823529412f,0.9627450980392158f,0.9784313725490197f,0.9941176470588236f,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9862745098039216f,0.9705882352941178f,0.9549019607843139f,0.93921568627451f,0.9235294117647062f,0.9078431372549018f,0.892156862745098f,0.8764705882352941f,0.8607843137254902f,0.8450980392156864f,0.8294117647058825f,0.8137254901960786f,0.7980392156862743f,0.7823529411764705f,0.7666666666666666f,0.7509803921568627f,0.7352941176470589f,0.719607843137255f,0.7039215686274511f,0.6882352941176473f,0.6725490196078434f,0.6568627450980391f,0.6411764705882352f,0.6254901960784314f,0.6098039215686275f,0.5941176470588236f,0.5784313725490198f,0.5627450980392159f,0.5470588235294116f,0.5313725490196077f,0.5156862745098039f,0.5f };
const float g[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001960784313725483f,0.01764705882352935f,0.03333333333333333f,0.0490196078431373f,0.06470588235294117f,0.08039215686274503f,0.09607843137254901f,0.111764705882353f,0.1274509803921569f,0.1431372549019607f,0.1588235294117647f,0.1745098039215687f,0.1901960784313725f,0.2058823529411764f,0.2215686274509804f,0.2372549019607844f,0.2529411764705882f,0.2686274509803921f,0.2843137254901961f,0.3f,0.3156862745098039f,0.3313725490196078f,0.3470588235294118f,0.3627450980392157f,0.3784313725490196f,0.3941176470588235f,0.4098039215686274f,0.4254901960784314f,0.4411764705882353f,0.4568627450980391f,0.4725490196078431f,0.4882352941176471f,0.503921568627451f,0.5196078431372548f,0.5352941176470587f,0.5509803921568628f,0.5666666666666667f,0.5823529411764705f,0.5980392156862746f,0.6137254901960785f,0.6294117647058823f,0.6450980392156862f,0.6607843137254901f,0.6764705882352942f,0.692156862745098f,0.7078431372549019f,0.723529411764706f,0.7392156862745098f,0.7549019607843137f,0.7705882352941176f,0.7862745098039214f,0.8019607843137255f,0.8176470588235294f,0.8333333333333333f,0.8490196078431373f,0.8647058823529412f,0.8803921568627451f,0.8960784313725489f,0.9117647058823528f,0.9274509803921569f,0.9431372549019608f,0.9588235294117646f,0.9745098039215687f,0.9901960784313726f,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9901960784313726f,0.9745098039215687f,0.9588235294117649f,0.943137254901961f,0.9274509803921571f,0.9117647058823528f,0.8960784313725489f,0.8803921568627451f,0.8647058823529412f,0.8490196078431373f,0.8333333333333335f,0.8176470588235296f,0.8019607843137253f,0.7862745098039214f,0.7705882352941176f,0.7549019607843137f,0.7392156862745098f,0.723529411764706f,0.7078431372549021f,0.6921568627450982f,0.6764705882352944f,0.6607843137254901f,0.6450980392156862f,0.6294117647058823f,0.6137254901960785f,0.5980392156862746f,0.5823529411764707f,0.5666666666666669f,0.5509803921568626f,0.5352941176470587f,0.5196078431372548f,0.503921568627451f,0.4882352941176471f,0.4725490196078432f,0.4568627450980394f,0.4411764705882355f,0.4254901960784316f,0.4098039215686273f,0.3941176470588235f,0.3784313725490196f,0.3627450980392157f,0.3470588235294119f,0.331372549019608f,0.3156862745098041f,0.2999999999999998f,0.284313725490196f,0.2686274509803921f,0.2529411764705882f,0.2372549019607844f,0.2215686274509805f,0.2058823529411766f,0.1901960784313728f,0.1745098039215689f,0.1588235294117646f,0.1431372549019607f,0.1274509803921569f,0.111764705882353f,0.09607843137254912f,0.08039215686274526f,0.06470588235294139f,0.04901960784313708f,0.03333333333333321f,0.01764705882352935f,0.001960784313725483f,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
const float b[] = { 0.5f,0.5156862745098039f,0.5313725490196078f,0.5470588235294118f,0.5627450980392157f,0.5784313725490196f,0.5941176470588235f,0.6098039215686275f,0.6254901960784314f,0.6411764705882352f,0.6568627450980392f,0.6725490196078432f,0.6882352941176471f,0.7039215686274509f,0.7196078431372549f,0.7352941176470589f,0.7509803921568627f,0.7666666666666666f,0.7823529411764706f,0.7980392156862746f,0.8137254901960784f,0.8294117647058823f,0.8450980392156863f,0.8607843137254902f,0.8764705882352941f,0.892156862745098f,0.907843137254902f,0.9235294117647059f,0.9392156862745098f,0.9549019607843137f,0.9705882352941176f,0.9862745098039216f,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9941176470588236f,0.9784313725490197f,0.9627450980392158f,0.9470588235294117f,0.9313725490196079f,0.915686274509804f,0.8999999999999999f,0.884313725490196f,0.8686274509803922f,0.8529411764705883f,0.8372549019607844f,0.8215686274509804f,0.8058823529411765f,0.7901960784313726f,0.7745098039215685f,0.7588235294117647f,0.7431372549019608f,0.7274509803921569f,0.7117647058823531f,0.696078431372549f,0.6803921568627451f,0.6647058823529413f,0.6490196078431372f,0.6333333333333333f,0.6176470588235294f,0.6019607843137256f,0.5862745098039217f,0.5705882352941176f,0.5549019607843138f,0.5392156862745099f,0.5235294117647058f,0.5078431372549019f,0.4921568627450981f,0.4764705882352942f,0.4607843137254903f,0.4450980392156865f,0.4294117647058826f,0.4137254901960783f,0.3980392156862744f,0.3823529411764706f,0.3666666666666667f,0.3509803921568628f,0.335294117647059f,0.3196078431372551f,0.3039215686274508f,0.2882352941176469f,0.2725490196078431f,0.2568627450980392f,0.2411764705882353f,0.2254901960784315f,0.2098039215686276f,0.1941176470588237f,0.1784313725490199f,0.1627450980392156f,0.1470588235294117f,0.1313725490196078f,0.115686274509804f,0.1000000000000001f,0.08431372549019622f,0.06862745098039236f,0.05294117647058805f,0.03725490196078418f,0.02156862745098032f,0.00588235294117645f,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
// Declare the lut (please note it has to be a 3xN Mat of CV_32F)
int N(sizeof(r)/sizeof(float));
cv::Mat lut(cv::Mat(cv::Size(3,N), CV_32F, cv::Scalar(0.0)));
for (int i(0); i < N; ++i) {
lut.at<float>(i, 0, 0) = 255.0 * b[i];
lut.at<float>(i, 1, 0) = 255.0 * g[i];
lut.at<float>(i, 2, 0) = 255.0 * r[i];
}
// Initialize the FlannBasedMatcher
auto index_params = new cv::flann::KDTreeIndexParams(5);
auto search_params = new cv::flann::SearchParams(50);
cv::FlannBasedMatcher matcher(index_params, search_params);
matcher.add(lut);
matcher.train();
// Convert the image pixels to perform the query (3xH*W Mat of CV_32F)
int QLEN(im.rows*im.cols);
cv::Mat query(cv::Mat(cv::Size(3, QLEN), CV_32F, cv::Scalar(0.0)));
int i(0);
for (int y(0); y < im.rows; ++y) {
for (int x(0); x < im.cols; ++x) {
query.at<float>(i, 0) = float(im.at<cv::Vec3b>(y, x)[0]);
query.at<float>(i, 1) = float(im.at<cv::Vec3b>(y, x)[1]);
query.at<float>(i, 2) = float(im.at<cv::Vec3b>(y, x)[2]);
++i;
}
}
// Lookup all image pixels
std::vector<cv::DMatch> matches;
matcher.match(query, matches);
// Reconstruct the greyscale image
cv::Mat im_grey(cv::Mat(cv::Size(1, QLEN), CV_32F, cv::Scalar(0.0)));
for (int i(0); i < QLEN; ++i) {
im_grey.at<float>(i, 0) = matches[i].trainIdx / 255.0;
}
im_grey = im_grey.reshape(0, {im.rows,im.cols});
Above is brilliant answer from Christoph Rackwitz! But this is a bit confusing due Python Notebook specifics. Here is a full code for conversion.
from matplotlib import colormaps # colormaps['jet'], colormaps['turbo']
from matplotlib.colors import LinearSegmentedColormap
from matplotlib._cm import _jet_data
def convert_jet_to_grey(img):
(height, width) = img.shape[:2]
cm = LinearSegmentedColormap("jet", _jet_data, N=2 ** 8)
# cm = colormaps['turbo'] swap with jet if you use turbo colormap instead
cm._init() # Must be called first. cm._lut data field created here
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
fm = cv2.FlannBasedMatcher(index_params, search_params)
# JET, BGR order, excluding special palette values (>= 256)
fm.add(255 * np.float32([cm._lut[:256, (2, 1, 0)]])) # jet
fm.train()
# look up all pixels
query = img.reshape((-1, 3)).astype(np.float32)
matches = fm.match(query)
# statistics: `result` is palette indices ("grayscale image")
output = np.uint16([m.trainIdx for m in matches]).reshape(height, width)
result = np.where(output < 256, output, 0).astype(np.uint8)
# dist = np.uint8([m.distance for m in matches]).reshape(height, width)
return result # , dist uncomment if you wish accuracy image

Detecting circles in an image using laplacian

I'm trying detect the disk and cup in the back of an eye (fundus) to calculate certain things later on. So here is an image of the eye:
I'm just trying to detect the disk, or the larger yellow-ish circle on the right side of the image, and the cup, or the smaller yellow circle inside that first circle, using OpenCV and python so I can eventually perform certain calculations.
So far, I've tried to use laplacian filtering to make the circle more prominent.
Here is my code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
from pylab import *
# loading image
img0 = cv2.imread('01_g.jpg',)
# converting to gray scale
gray = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
# remove noise
img = cv2.GaussianBlur(gray,(3,3),0)
# convolute with proper kernels
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5) # x
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5) # y
magnitude = sqrt(sobelx**2+sobely**2)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
This is the result I got:
As you can see, the laplacian filter didn't help at all-- in fact, the cup and disk aren't even visible. The sobelx and sobely at least gave some outline of the outer circle (disk)
I also have tried the absolute value of the laplacian:
final = np.absolute(laplacian)
plt.imshow(final, cmap = 'gray')
plt.show()
and I got this result:
I have also tried applying the Difference of Gaussians method using this code:
#difference of gaussians
blur1 = cv2.GaussianBlur(img,(3,3),1)
blur2 = cv2.GaussianBlur(img,(5,5),1.1)
difference = blur2 - blur1
plt.imshow(difference, cmap = 'gray')
plt.show()
But this also doesn't get me anywhere. I would really appreciate some help on how I might go about detecting the cup and disk in this image.
EDIT:
Previous MSER approach as shown fails to find a circular blob, though it highlights the region. So I tried Difference-of-Gaussians(DoG) for blob detection, and it gives good results. You can experiment with the gaussian kernel sizes and their sigmas. Note that I've downsampled the image and removed the vessel structures by dilation prior to applying the DoG. Thresholding the DoG image gives you the blobs.
Also I noticed that the region you are interested in is the global maximum of the image (it may not be so for a different image). May be you can combine this knowledge as well to your algorithm.
DoG
Thresholded DoG
Global Max
Code (c++) for Dog approach
Mat im = imread("8Lzuq.jpg", 0);
Mat dw;
pyrDown(im, dw);
pyrDown(dw, dw);
pyrDown(dw, dw);
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7, 7));
morphologyEx(dw, dw, CV_MOP_DILATE, kernel);
Mat g1, g2, dog, bw;
GaussianBlur(dw, g1, Size(31, 31), 21, 21);
GaussianBlur(dw, g2, Size(65, 65), 31, 31);
dog = g1 - g2;
normalize(dog, dog, 0, 255, NORM_MINMAX);
threshold(dog, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Point mx;
minMaxLoc(dw, NULL, NULL, NULL, &mx);
circle(dw, mx, 20, Scalar(255, 255, 255), 2);
MSER approach
I tried downsampling the color image, dilating it, then detecting MSERs in individual channels. The result looks good though it doesn't outline the disk as a perfect circle.
Blue channel:
Green channel:
Red channel:
Detecting MSERs in the color image didn't work well.
Code in c++
Mat im = imread("8Lzuq.jpg");
Mat dw;
pyrDown(im, dw);
pyrDown(dw, dw);
pyrDown(dw, dw);
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7, 7));
morphologyEx(dw, dw, CV_MOP_DILATE, kernel);
Mat ch[3];
split(dw, ch);
MSER mser;
vector<vector<Point>> regions;
mser(ch[2], regions);
Mat regionsMat = Mat::zeros(dw.rows, dw.cols, CV_8U);
for (size_t i = 0; i < regions.size(); i++)
{
for (Point pt: regions[i])
{
uchar& val = regionsMat.at<uchar>(pt);
if (val > 0)
{
val += 1;
}
else
{
val = 1;
}
}
}
imwrite("reg.jpg", regionsMat*50);

Simple Digit Recognition OCR in OpenCV-Python

I am trying to implement a "Digit Recognition OCR" in OpenCV-Python (cv2). It is just for learning purposes. I would like to learn both KNearest and SVM features in OpenCV.
I have 100 samples (i.e. images) of each digit. I would like to train with them.
There is a sample letter_recog.py that comes with OpenCV sample. But I still couldn't figure out on how to use it. I don't understand what are the samples, responses etc. Also, it loads a txt file at first, which I didn't understand first.
Later on searching a little bit, I could find a letter_recognition.data in cpp samples. I used it and made a code for cv2.KNearest in the model of letter_recog.py (just for testing):
import numpy as np
import cv2
fn = 'letter-recognition.data'
a = np.loadtxt(fn, np.float32, delimiter=',', converters={ 0 : lambda ch : ord(ch)-ord('A') })
samples, responses = a[:,1:], a[:,0]
model = cv2.KNearest()
retval = model.train(samples,responses)
retval, results, neigh_resp, dists = model.find_nearest(samples, k = 10)
print results.ravel()
It gave me an array of size 20000, I don't understand what it is.
Questions:
1) What is letter_recognition.data file? How to build that file from my own data set?
2) What does results.reval() denote?
3) How we can write a simple digit recognition tool using letter_recognition.data file (either KNearest or SVM)?
Well, I decided to workout myself on my question to solve the above problem. What I wanted is to implement a simple OCR using KNearest or SVM features in OpenCV. And below is what I did and how. (it is just for learning how to use KNearest for simple OCR purposes).
1) My first question was about letter_recognition.data file that comes with OpenCV samples. I wanted to know what is inside that file.
It contains a letter, along with 16 features of that letter.
And this SOF helped me to find it. These 16 features are explained in the paper Letter Recognition Using Holland-Style Adaptive Classifiers.
(Although I didn't understand some of the features at the end)
2) Since I knew, without understanding all those features, it is difficult to do that method. I tried some other papers, but all were a little difficult for a beginner.
So I just decided to take all the pixel values as my features. (I was not worried about accuracy or performance, I just wanted it to work, at least with the least accuracy)
I took the below image for my training data:
(I know the amount of training data is less. But, since all letters are of the same font and size, I decided to try on this).
To prepare the data for training, I made a small code in OpenCV. It does the following things:
It loads the image.
Selects the digits (obviously by contour finding and applying constraints on area and height of letters to avoid false detections).
Draws the bounding rectangle around one letter and wait for key press manually. This time we press the digit key ourselves corresponding to the letter in the box.
Once the corresponding digit key is pressed, it resizes this box to 10x10 and saves all 100 pixel values in an array (here, samples) and corresponding manually entered digit in another array(here, responses).
Then save both the arrays in separate .txt files.
At the end of the manual classification of digits, all the digits in the training data (train.png) are labeled manually by ourselves, image will look like below:
Below is the code I used for the above purpose (of course, not so clean):
import sys
import numpy as np
import cv2
im = cv2.imread('pitrain.png')
im3 = im.copy()
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
################# Now finding Contours ###################
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
samples = np.empty((0,100))
responses = []
keys = [i for i in range(48,58)]
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,0,255),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
cv2.imshow('norm',im)
key = cv2.waitKey(0)
if key == 27: # (escape to quit)
sys.exit()
elif key in keys:
responses.append(int(chr(key)))
sample = roismall.reshape((1,100))
samples = np.append(samples,sample,0)
responses = np.array(responses,np.float32)
responses = responses.reshape((responses.size,1))
print "training complete"
np.savetxt('generalsamples.data',samples)
np.savetxt('generalresponses.data',responses)
Now we enter in to training and testing part.
For the testing part, I used the below image, which has the same type of letters I used for the training phase.
For training we do as follows:
Load the .txt files we already saved earlier
create an instance of the classifier we are using (it is KNearest in this case)
Then we use KNearest.train function to train the data
For testing purposes, we do as follows:
We load the image used for testing
process the image as earlier and extract each digit using contour methods
Draw a bounding box for it, then resize it to 10x10, and store its pixel values in an array as done earlier.
Then we use KNearest.find_nearest() function to find the nearest item to the one we gave. ( If lucky, it recognizes the correct digit.)
I included last two steps (training and testing) in single code below:
import cv2
import numpy as np
####### training part ###############
samples = np.loadtxt('generalsamples.data',np.float32)
responses = np.loadtxt('generalresponses.data',np.float32)
responses = responses.reshape((responses.size,1))
model = cv2.KNearest()
model.train(samples,responses)
############################# testing part #########################
im = cv2.imread('pi.png')
out = np.zeros(im.shape,np.uint8)
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
roismall = roismall.reshape((1,100))
roismall = np.float32(roismall)
retval, results, neigh_resp, dists = model.find_nearest(roismall, k = 1)
string = str(int((results[0][0])))
cv2.putText(out,string,(x,y+h),0,1,(0,255,0))
cv2.imshow('im',im)
cv2.imshow('out',out)
cv2.waitKey(0)
And it worked, below is the result I got:
Here it worked with 100% accuracy. I assume this is because all the digits are of the same kind and the same size.
But anyway, this is a good start to go for beginners (I hope so).
For those who interested in C++ code can refer below code.
Thanks Abid Rahman for the nice explanation.
The procedure is same as above but, the contour finding uses only first hierarchy level contour, so that the algorithm uses only outer contour for each digit.
Code for creating sample and Label data
//Process image to extract contour
Mat thr,gray,con;
Mat src=imread("digit.png",1);
cvtColor(src,gray,CV_BGR2GRAY);
threshold(gray,thr,200,255,THRESH_BINARY_INV); //Threshold to find contour
thr.copyTo(con);
// Create sample and label data
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
Mat sample;
Mat response_array;
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); //Find contour
for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through first hierarchy level contours
{
Rect r= boundingRect(contours[i]); //Find bounding rect for each contour
rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,0,255),2,8,0);
Mat ROI = thr(r); //Crop the image
Mat tmp1, tmp2;
resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR ); //resize to 10X10
tmp1.convertTo(tmp2,CV_32FC1); //convert to float
sample.push_back(tmp2.reshape(1,1)); // Store sample data
imshow("src",src);
int c=waitKey(0); // Read corresponding label for contour from keyoard
c-=0x30; // Convert ascii to intiger value
response_array.push_back(c); // Store label to a mat
rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,255,0),2,8,0);
}
// Store the data to file
Mat response,tmp;
tmp=response_array.reshape(1,1); //make continuous
tmp.convertTo(response,CV_32FC1); // Convert to float
FileStorage Data("TrainingData.yml",FileStorage::WRITE); // Store the sample data in a file
Data << "data" << sample;
Data.release();
FileStorage Label("LabelData.yml",FileStorage::WRITE); // Store the label data in a file
Label << "label" << response;
Label.release();
cout<<"Training and Label data created successfully....!! "<<endl;
imshow("src",src);
waitKey();
Code for training and testing
Mat thr,gray,con;
Mat src=imread("dig.png",1);
cvtColor(src,gray,CV_BGR2GRAY);
threshold(gray,thr,200,255,THRESH_BINARY_INV); // Threshold to create input
thr.copyTo(con);
// Read stored sample and label for training
Mat sample;
Mat response,tmp;
FileStorage Data("TrainingData.yml",FileStorage::READ); // Read traing data to a Mat
Data["data"] >> sample;
Data.release();
FileStorage Label("LabelData.yml",FileStorage::READ); // Read label data to a Mat
Label["label"] >> response;
Label.release();
KNearest knn;
knn.train(sample,response); // Train with sample and responses
cout<<"Training compleated.....!!"<<endl;
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
//Create input sample by contour finding and cropping
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
Mat dst(src.rows,src.cols,CV_8UC3,Scalar::all(0));
for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour for first hierarchy level .
{
Rect r= boundingRect(contours[i]);
Mat ROI = thr(r);
Mat tmp1, tmp2;
resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR );
tmp1.convertTo(tmp2,CV_32FC1);
float p=knn.find_nearest(tmp2.reshape(1,1), 1);
char name[4];
sprintf(name,"%d",(int)p);
putText( dst,name,Point(r.x,r.y+r.height) ,0,1, Scalar(0, 255, 0), 2, 8 );
}
imshow("src",src);
imshow("dst",dst);
imwrite("dest.jpg",dst);
waitKey();
Result
In the result the dot in the first line is detected as 8 and we haven’t trained for dot. Also I am considering every contour in first hierarchy level as the sample input, user can avoid it by computing the area.
I had some problems to generate the training data, because it was hard sometimes to identify the last selected letter, so I rotated the image 1.5 degrees. Now each character is selected in order and the test still shows a 100% accuracy rate after training. Here is the code:
import numpy as np
import cv2
def rotate_image(image, angle):
image_center = tuple(np.array(image.shape[1::-1]) / 2)
rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
return result
img = cv2.imread('training_image.png')
cv2.imshow('orig image', img)
whiteBorder = [255,255,255]
# extend the image border
image1 = cv2.copyMakeBorder(img, 80, 80, 80, 80, cv2.BORDER_CONSTANT, None, whiteBorder)
# rotate the image 1.5 degrees clockwise for ease of data entry
image_rot = rotate_image(image1, -1.5)
#crop_img = image_rot[y:y+h, x:x+w]
cropped = image_rot[70:350, 70:710]
cv2.imwrite('rotated.png', cropped)
cv2.imshow('rotated image', cropped)
cv2.waitKey(0)
For sample data, I made some changes to the script, like this:
import sys
import numpy as np
import cv2
def sort_contours(contours, x_axis_sort='LEFT_TO_RIGHT', y_axis_sort='TOP_TO_BOTTOM'):
# initialize the reverse flag
x_reverse = False
y_reverse = False
if x_axis_sort == 'RIGHT_TO_LEFT':
x_reverse = True
if y_axis_sort == 'BOTTOM_TO_TOP':
y_reverse = True
boundingBoxes = [cv2.boundingRect(c) for c in contours]
# sorting on x-axis
sortedByX = zip(*sorted(zip(contours, boundingBoxes),
key=lambda b:b[1][0], reverse=x_reverse))
# sorting on y-axis
(contours, boundingBoxes) = zip(*sorted(zip(*sortedByX),
key=lambda b:b[1][1], reverse=y_reverse))
# return the list of sorted contours and bounding boxes
return (contours, boundingBoxes)
im = cv2.imread('rotated.png')
im3 = im.copy()
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours, boundingBoxes = sort_contours(contours, x_axis_sort='LEFT_TO_RIGHT', y_axis_sort='TOP_TO_BOTTOM')
samples = np.empty((0,100))
responses = []
keys = [i for i in range(48,58)]
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28 and h < 40:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,0,255),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
cv2.imshow('norm',im)
key = cv2.waitKey(0)
if key == 27: # (escape to quit)
sys.exit()
elif key in keys:
responses.append(int(chr(key)))
sample = roismall.reshape((1,100))
samples = np.append(samples,sample,0)
responses = np.array(responses,np.ubyte)
responses = responses.reshape((responses.size,1))
print("training complete")
np.savetxt('generalsamples.data',samples,fmt='%i')
np.savetxt('generalresponses.data',responses,fmt='%i')

Categories

Resources