9

I am using SVM to predict my ROI, I trained SVM and now in testing phases, it's giving me output with labels in the form of 1 and 0.

I am trying that if SVM predicts 1 mean image contains eyebrow, now I want that it should be rectangle around the eyebrow because the algorithm is predicting based on the eyebrow. How can I do this? Below is the code I use for prediction.

h_og = cv2.HOGDescriptor()
histogram = h_og.compute(photo)
arr.append(histogram)
arr = np.float32(arr)
result = svm.predict(arr)

Now the result is in the form of number or label. How I can draw a rectangle on that ROI of testing image.

Positive data: image with eyebrows
Negative data: images not containing eyebrow
Testing data: Full face of the person

If I have to use detectMultiScale() with it how I will use it with the above logic.

Code use for training purpose

sam = []
lab = []    
# Get positive samples
for filename in glob.glob('D:\*.png'):
    im = cv2.imread(filename, 1)
    h_og = cv2.HOGDescriptor()
    hist = h_og.compute(im)
    sam.append(hist)
    lab.append(1)

# Get negative samples
for file in glob.glob('D:\\*.png'):
    im = cv2.imread(file, 1)
    im = cv2.resize(img, (240, 160))
    h_og = cv2.HOGDescriptor()
    hist = h_og.compute(im)
    sam.append(hist)
    lab.append(0)

# Convert objects to Numpy Objects
sam = np.float32(sam)
lab = np.array(lab)


# Shuffle Samples
rand = np.random.RandomState(321)
shuffle = rand.permutation(len(sam))
sam = sam[shuffle]
lab = lab[shuffle]    


svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
#svm.setKernel(cv2.ml.SVM_RBF)
cv2.ml.SVM_LINEAR
# svm.setDegree(0.0)
svm.setGamma(5.383)
# svm.setCoef0(0.0)
svm.setC(2.67)
# svm.setNu(0.0)
# svm.setP(0.0)
# svm.setClassWeights(None)

svm.train(sam, cv2.ml.ROW_SAMPLE, lab) 
svm.save('file.xml')

More Explanation:

If my image contains eyebrow, my SVM prediction is successfully returning me 1 But after that I want it to display that image the below way, the rectangle should be on the coordinates based on which SVM predicts 1

enter image description here

Above image is just a sample image, I am doing it for eyebrow and after prediction, I want to achieve or trying to achieve the output in the above way.

AHF
  • 1,070
  • 2
  • 15
  • 47
  • Possible duplicate of [How to crop an image in OpenCV using Python](https://stackoverflow.com/questions/15589517/how-to-crop-an-image-in-opencv-using-python) – T A Jul 26 '19 at 12:28
  • My data convert it into float form, I am not just simply drawing the rectangle, Its about first I am predicting and wants to draw a rectangle based on that prediction. – AHF Jul 26 '19 at 12:30
  • 2
    Then you should first ask something like: How do I get the ROI coordinates based on my prediction? – T A Jul 26 '19 at 12:32
  • Yes this comment is better predicting what I am trying to do or asking for help – AHF Jul 26 '19 at 12:33
  • 1
    Reporting what I said in the chat, negative labels should be -1. – Doch88 Aug 09 '19 at 15:44
  • Even after `-1` its drawing rectangle among whole image, not the eyebrow Maybe, do I need to change the values of the descriptors `hog = cv2.HOGDescriptor((160,320), (16,16), (8,8), (8,8), 9)` – AHF Aug 12 '19 at 16:12

1 Answers1

4

svm.predict(arr) can only predict a single image.

To get coordinates and ROIs you need something that uses this method with different parts of the image at different scales.

So yes, you need to use detectMultiScale(). It is a method of cv2.HOGDescriptor(), therefore you first need to set h_og.setSVMDetector(array) with the SVM supports vectors and the rho that you have trained. You can get them using svm.getSupportVectors() and svm.getDecisionFunction(0).

After that, with found, w = h_og.detectMultiScale(img) you will have a list of rects (found) containing positive data that you can use to draw your boxes.


Try something like this, for example:

hog = cv2.HOGDescriptor() 
svm = cv2.ml.SVM_load('svm.xml')

sv = svm.getSupportVectors()
rho, alpha, svidx = svm.getDecisionFunction(0)
svm_new = np.append(sv, -rho)
hog.setSVMDetector(svm_new)

for file in glob.glob("Testing\\*.jpg"): 
    img = cv2.imread(file, 1) 
    img = cv2.resize(img, (240, 160))
    found, w = hog.detectMultiScale(img)

    for (x, y, w, h) in found:
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv2.imshow("Image", img)
    cv2.waitKey()

Alternatively, try following this example. Save the model of the SVM using this:

svm.save("svm.xml")
tree = ET.parse('svm.xml')
root = tree.getroot()

SVs = root.getchildren()[0].getchildren()[-2].getchildren()[0] 
rho = float( root.getchildren()[0].getchildren()[-1].getchildren()[0].getchildren()[1].text )
svmvec = [float(x) for x in re.sub( '\s+', ' ', SVs.text ).strip().split(' ')]
svmvec.append(-rho)
pickle.dump(svmvec, open("svm.pickle", 'w'))

In order to do this, you need to import pickle and ElementTree of XML:

import xml.etree.ElementTree as ET
import pickle

And then load it and use it with this:

svm = pickle.load(open("svm.pickle"))
hog.setSVMDetector( np.array(svm) )

found, w = hog.detectMultiScale(img)
Doch88
  • 728
  • 1
  • 8
  • 22
  • Do we need to use `h_og.setSVMDetector(svm)` after the prediction part or in training part – AHF Jul 29 '19 at 14:44
  • `h_og.detectMultiScale(img)` is the actual prediction part, so you need to set the SVM before doing this command. – Doch88 Jul 29 '19 at 15:13
  • I have edited the post, you cannot put the instance of the SVm in setSVMDetector(); you must put support vectors and rho there. – Doch88 Jul 29 '19 at 15:23
  • `svm = cv2.ml.SVM_load('svm.xml')` `sam = []` `for file in glob.glob("Testing\\*.jpg"): img = cv2.imread(file, 1) img = cv2.resize(img, (240, 160)) hog = cv2.HOGDescriptor() hog = cv2.HOGDescriptor((32,64), (16,16), (8,8), (8,8), 9) hist = hog.compute(img) sample.append(hist) hog.setSVMDetector(array) sam = np.float32(sam) res = svm.predict(sam) del svm found, w = hog.detectMultiScale(img) print (res)` – AHF Jul 29 '19 at 15:42
  • No, you are not initializing "array" in your code. You need to get Support Vectors from the SVM and put them in the HOGDescriptor, like what you do with a pretrained People Detector (i.e. https://github.com/abhisharma7/FacePoseEstimation/blob/a5a50ecdbe3a139d8c2a4ccec9904bcb1944b421/facepose_detection.py#L153 ). To get them you can use the related method of the svm instance. – Doch88 Jul 29 '19 at 15:57
  • Try something like the code I just added to the answer. Adapt it for your case, of course. – Doch88 Jul 30 '19 at 11:32
  • I tried `hog.setSVMDetector(svm.getSupportVectors()) cv2.error: OpenCV(4.1.0) C:\projects\opencv-python\opencv\modules\core\src\matrix_wrap.cpp:1365: error: (-215:Assertion failed) d == 2 && (sizes[0] == 1 || sizes[1] == 1 || sizes[0]*sizes[1] == 0) in function 'cv::_OutputArray::create'` – AHF Jul 30 '19 at 15:11
  • Unfortunately, documentation about this argument is very poor. I forgot to add the decision function, try the updated code on the answer. – Doch88 Jul 30 '19 at 15:41
  • I checked this method last week `rho, alpha, svidx = cv.ml_SVM.getDecisionFunction(0)` its from cv, i am using cv2. – AHF Jul 30 '19 at 16:32
  • No, it's from cv2. I only typed it wrong because I copied it from the documentation. It is a method of cv::ml::SVM, therefore you just need to use it with the SVM that you have loaded. `svm.getDecisionFunction(0)` – Doch88 Jul 30 '19 at 18:19
  • cv2 simply means that you are using the C++ library. And as you can see [there](https://github.com/opencv/opencv/blob/master/modules/ml/include/opencv2/ml.hpp#L791) this method is wrapped (CV_WRAP) for the use with python, and it's C++. – Doch88 Jul 30 '19 at 18:33
  • What type of error do you have? Could you post your new code? – Doch88 Jul 30 '19 at 18:46
  • `hog.setSVMDetector(svm_new) cv2.error: OpenCV(4.1.0) C:\projects\opencv-python\opencv\modules\objdetect\src\hog.cpp:115: error: (-215:Assertion failed) checkDetectorSize() in function 'cv::HOGDescriptor::setSVMDetector'` – AHF Jul 30 '19 at 18:55
  • If I comment `#hog.setSVMDetector(svm_new)` this line, code run without error but not getting the required output, – AHF Jul 30 '19 at 18:59
  • Wait, you are using the code of my answer? If so, you must set the parameters of HOGDescriptor like the ones you used in the training. So use hog = cv2.HOGDescriptor() instead of my example. – Doch88 Jul 30 '19 at 19:10
  • I used the hog=cv2.HOGDescriptor() both ways with argument and without argument but still no luck !!! – AHF Jul 31 '19 at 17:15
  • Mmh strange, I don't know, try the alternative that I wrote then. – Doch88 Jul 31 '19 at 19:03
  • Do I need to change any code in my learning part `hist = h_og.compute(im) sam.append(hist) lab.append(0)` – AHF Jul 31 '19 at 21:17
  • No, you don't need to change your training. – Doch88 Aug 01 '19 at 08:45
  • It is running but not drawing a rectangle on the region of interest but drawing on the whole image even negative – AHF Aug 01 '19 at 21:38
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/197367/discussion-on-answer-by-doch88-how-do-i-get-the-roi-coordinates-based-on-my-pred). – Samuel Liew Aug 01 '19 at 23:16