I extracted the contours of an image, that you can see here:
However, it has some noise.
How can I smooth the noise? I did a close up to make clearer what I want to meant
Original image that I've used:
Code:
rMaskgray = cv2.imread('redmask.jpg', cv2.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, binRed) = cv2.threshold(rMaskgray, 50, 255, cv2.THRESH_BINARY)
Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.max(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255
for c in Rcontours:
if(( cv2.contourArea(c) > max_rarea * 0.70) and (cv2.contourArea(c)< max_rarea)):
cv2.drawContours(CntExternalMask,[c],-1,0,1)
cv2.imwrite('contour1.jpg', CntExternalMask)
Try an upgrade to OpenCV 3.1.0. After some code adaptations for the new version as shown below, I tried it out with OpenCV version 3.1.0 and did not see any of the effects you are describing.
import cv2
import numpy as np
print cv2.__version__
rMaskgray = cv2.imread('5evOn.jpg', 0)
(thresh, binRed) = cv2.threshold(rMaskgray, 50, 255, cv2.THRESH_BINARY)
_, Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.max(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255
for c in Rcontours:
if(( cv2.contourArea(c) > max_rarea * 0.70) and (cv2.contourArea(c)< max_rarea)):
cv2.drawContours(CntExternalMask,[c],-1,0,1)
cv2.imwrite('contour1.jpg', CntExternalMask)
I don't know if is it ok to provide Java code - but I implemented Gaussian smoothing for openCV contour. Logic and theory is taken from here https://www.morethantechnical.com/2012/12/07/resampling-smoothing-and-interest-points-of-curves-via-css-in-opencv-w-code/
package CurveTools;
import org.apache.log4j.Logger;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import java.util.ArrayList;
import java.util.List;
import static org.opencv.core.CvType.CV_64F;
import static org.opencv.imgproc.Imgproc.getGaussianKernel;
class CurveSmoother {
private double[] g, dg, d2g, gx, dx, d2x;
private double gx1, dgx1, d2gx1;
public double[] kappa, smoothX, smoothY;
public double[] contourX, contourY;
/* 1st and 2nd derivative of 1D gaussian */
void getGaussianDerivs(double sigma, int M) {
int L = (M - 1) / 2;
double sigma_sq = sigma * sigma;
double sigma_quad = sigma_sq * sigma_sq;
dg = new double[M];
d2g = new double[M];
g = new double[M];
Mat tmpG = getGaussianKernel(M, sigma, CV_64F);
for (double i = -L; i < L + 1.0; i += 1.0) {
int idx = (int) (i + L);
g[idx] = tmpG.get(idx, 0)[0];
// from http://www.cedar.buffalo.edu/~srihari/CSE555/Normal2.pdf
dg[idx] = -i * g[idx] / sigma_sq;
d2g[idx] = (-sigma_sq + i * i) * g[idx] / sigma_quad;
}
}
/* 1st and 2nd derivative of smoothed curve point */
void getdX(double[] x, int n, double sigma, boolean isOpen) {
int L = (g.length - 1) / 2;
gx1 = dgx1 = d2gx1 = 0.0;
for (int k = -L; k < L + 1; k++) {
double x_n_k;
if (n - k < 0) {
if (isOpen) {
//open curve - mirror values on border
x_n_k = x[-(n - k)];
} else {
//closed curve - take values from end of curve
x_n_k = x[x.length + (n - k)];
}
} else if (n - k > x.length - 1) {
if (isOpen) {
//mirror value on border
x_n_k = x[n + k];
} else {
x_n_k = x[(n - k) - x.length];
}
} else {
x_n_k = x[n - k];
}
gx1 += x_n_k * g[k + L]; //gaussians go [0 -> M-1]
dgx1 += x_n_k * dg[k + L];
d2gx1 += x_n_k * d2g[k + L];
}
}
/* 0th, 1st and 2nd derivatives of whole smoothed curve */
void getdXcurve(double[] x, double sigma, boolean isOpen) {
gx = new double[x.length];
dx = new double[x.length];
d2x = new double[x.length];
for (int i = 0; i < x.length; i++) {
getdX(x, i, sigma, isOpen);
gx[i] = gx1;
dx[i] = dgx1;
d2x[i] = d2gx1;
}
}
/*
compute curvature of curve after gaussian smoothing
from "Shape similarity retrieval under affine transforms", Mokhtarian & Abbasi 2002
curvex - x position of points
curvey - y position of points
kappa - curvature coeff for each point
sigma - gaussian sigma
*/
void computeCurveCSS(double[] curvex, double[] curvey, double sigma, boolean isOpen) {
int M = (int) Math.round((10.0 * sigma + 1.0) / 2.0) * 2 - 1;
assert (M % 2 == 1); //M is an odd number
getGaussianDerivs(sigma, M);//, g, dg, d2g
double[] X, XX, Y, YY;
getdXcurve(curvex, sigma, isOpen);
smoothX = gx.clone();
X = dx.clone();
XX = d2x.clone();
getdXcurve(curvey, sigma, isOpen);
smoothY = gx.clone();
Y = dx.clone();
YY = d2x.clone();
kappa = new double[curvex.length];
for (int i = 0; i < curvex.length; i++) {
// Mokhtarian 02' eqn (4)
kappa[i] = (X[i] * YY[i] - XX[i] * Y[i]) / Math.pow(X[i] * X[i] + Y[i] * Y[i], 1.5);
}
}
/* find zero crossings on curvature */
ArrayList<Integer> findCSSInterestPoints() {
assert (kappa != null);
ArrayList<Integer> crossings = new ArrayList<>();
for (int i = 0; i < kappa.length - 1; i++) {
if ((kappa[i] < 0.0 && kappa[i + 1] > 0.0) || kappa[i] > 0.0 && kappa[i + 1] < 0.0) {
crossings.add(i);
}
}
return crossings;
}
public void polyLineSplit(MatOfPoint pl) {
contourX = new double[pl.height()];
contourY = new double[pl.height()];
for (int j = 0; j < contourX.length; j++) {
contourX[j] = pl.get(j, 0)[0];
contourY[j] = pl.get(j, 0)[1];
}
}
public MatOfPoint polyLineMerge(double[] xContour, double[] yContour) {
assert (xContour.length == yContour.length);
MatOfPoint pl = new MatOfPoint();
List<Point> list = new ArrayList<>();
for (int j = 0; j < xContour.length; j++)
list.add(new Point(xContour[j], yContour[j]));
pl.fromList(list);
return pl;
}
MatOfPoint smoothCurve(MatOfPoint curve, double sigma) {
int M = (int) Math.round((10.0 * sigma + 1.0) / 2.0) * 2 - 1;
assert (M % 2 == 1); //M is an odd number
//create kernels
getGaussianDerivs(sigma, M);
polyLineSplit(curve);
getdXcurve(contourX, sigma, false);
smoothX = gx.clone();
getdXcurve(contourY, sigma, false);
smoothY = gx;
Logger.getRootLogger().info("Smooth curve len: " + smoothX.length);
return polyLineMerge(smoothX, smoothY);
}
}
Related
I would like to reimplement the Qt C++ "Surface" example (Q3DSurface) in PySide2 but QSurfaceDataArray and QSurfaceDataRow are not available.
void SurfaceGraph::fillSqrtSinProxy()
{
float stepX = (sampleMax - sampleMin) / float(sampleCountX - 1);
float stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1);
QSurfaceDataArray *dataArray = new QSurfaceDataArray;
dataArray->reserve(sampleCountZ);
for (int i = 0 ; i < sampleCountZ ; i++) {
QSurfaceDataRow *newRow = new QSurfaceDataRow(sampleCountX);
// Keep values within range bounds, since just adding step can cause minor drift due
// to the rounding errors.
float z = qMin(sampleMax, (i * stepZ + sampleMin));
int index = 0;
for (int j = 0; j < sampleCountX; j++) {
float x = qMin(sampleMax, (j * stepX + sampleMin));
float R = qSqrt(z * z + x * x) + 0.01f;
float y = (qSin(R) / R + 0.24f) * 1.61f;
(*newRow)[index++].setPosition(QVector3D(x, y, z));
}
*dataArray << newRow;
}
m_sqrtSinProxy->resetArray(dataArray);
}
Is there are way to use a QVector<QSurfaceDataItem> in PySide2?
from PySide2.QtDataVisualization import QtDataVisualization as QDV
data_item = QDV.QSurfaceDataItem()
data_item.setPosition(QVector3D(x, y, z))
The QSurfaceDataItem is available but I can't pass the objects to QSurfaceDataProxy without QVector.
Using HoughTransform I am trying to detect boxes and provide for a distinct color.
So far my understanding is a box is horizontal and vertical line .
my code is
lines = cv2.HoughLines(edges,1,np.pi/180, 50)
# The below for loop runs till r and theta values
# are in the range of the 2d array
for r,theta in lines[0]:
# Stores the value of cos(theta) in a
a = np.cos(theta)
# Stores the value of sin(theta) in b
b = np.sin(theta)
# x0 stores the value rcos(theta)
x0 = a*r
# y0 stores the value rsin(theta)
y0 = b*r
# x1 stores the rounded off value of (rcos(theta)-1000sin(theta))
x1 = int(x0 + 1000*(-b))
# y1 stores the rounded off value of (rsin(theta)+1000cos(theta))
y1 = int(y0 + 1000*(a))
# x2 stores the rounded off value of (rcos(theta)+1000sin(theta))
x2 = int(x0 - 1000*(-b))
# y2 stores the rounded off value of (rsin(theta)-1000cos(theta))
y2 = int(y0 - 1000*(a))
# cv2.line draws a line in img from the point(x1,y1) to (x2,y2).
# (255,255,255) denotes the colour of the line to be.
cv2.line(img,(x1,y1), (x2,y2), (255,255,255),2) `
What could i do so that the boxes can be colored or identified?
You should do vertical and horizontal line detection separately so that you can make them each more specific.
Go through all your lines and compile a list of intersections between horizontal and vertical line combinations
Now you have a list of 2d points that if you draw them should pretty much be on the corners of the boxes. The final step is to collect those points into meaningful sets.
To get those sets, I would start with the point nearest origin (just for the sake of starting somewhere). I would look through all the other points for the closest other point that has a greater x but is withing +-5 (or some configurable range) y of the starting point. Then do the same but in the y direction. You now have the bottom corner of the box. Which you could just complete and start your ocr on, but to be more robust, find the final corner as well.
Once all 4 corners are found, remove all of those points from your intersection array and add however you want to denote box locations into a new array. Rinse and repeat as now a different point will be nearest origin. Without actually testing this, I think it will choke (or need some conditional improvement for missing walls) on the K box but should be pretty generic to variable box shapes and sizes.
Edit 1: In testing, I am finding that it will probably be difficult to separate the close corners of two adjacent boxes. I think a more generic and robust solution would be to after you get collisions, do a point clustering operation at about 1/3 box min side length. This will average corners together with their nearest neighbor. So this will slightly change the strategy as you will need to use every corner twice (box to left and box to right) except for end points.
Wrote up some test code and it is functional, here are the outputs:
Code, sorry for c++ and not at all optimized, happy friday :)
//CPP libaries
#include <stdio.h>
#include <mutex>
#include <thread>
//Included libraries
//Note: these headers have to be before any opencv due to a namespace collision (could probably be fixed)
#include <opencv2/opencv.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
// Finds the intersection of two lines, or returns false.
// The lines are defined by (o1, p1) and (o2, p2).
//https://stackoverflow.com/questions/7446126/opencv-2d-line-intersection-helper-function
bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2,
Point2f &r)
{
Point2f x = o2 - o1;
Point2f d1 = p1 - o1;
Point2f d2 = p2 - o2;
float cross = d1.x*d2.y - d1.y*d2.x;
if (abs(cross) < /*EPS*/1e-8)
return false;
double t1 = (x.x * d2.y - x.y * d2.x) / cross;
r = o1 + d1 * t1;
return true;
}
std::vector<Point2f> clusterPts(std::vector<Point2f> inputPts, double clusterRadius_Squared)
{
std::vector<Point2f> outputPts = std::vector<Point2f>();
while(inputPts.size()>0)
{
Point2f clusterCenter = inputPts[0];
while (true)
{
Point2f newClustCenter = Point2f(0, 0);
int averagingCount = 0;
std::vector<int> clusterIndicies = std::vector<int>();
for (int i = 0; i < inputPts.size(); i++)
{
if (clusterRadius_Squared >= pow(inputPts[i].x - clusterCenter.x, 2) + pow(inputPts[i].y - clusterCenter.y, 2))
{
newClustCenter.x += inputPts[i].x;
newClustCenter.y += inputPts[i].y;
averagingCount += 1;
clusterIndicies.push_back(i);
}
}
newClustCenter = newClustCenter / (double)averagingCount;
if (newClustCenter == clusterCenter)
{
//remove all points inside cluster from inputPts, stash cluster center, and break inner while loop
std::vector<Point2f> remainingPts = std::vector<Point2f>();
for (int i = 0; i < inputPts.size(); i++)
{
if (std::find(clusterIndicies.begin(), clusterIndicies.end(), i) == clusterIndicies.end())
{
remainingPts.push_back(inputPts[i]);
}
}
inputPts = remainingPts;
outputPts.push_back(clusterCenter);
break;
}
else
{
clusterCenter = newClustCenter;
}
}
}
return outputPts;
}
std::vector<Rect> findBoxes(std::vector<Point2f> corners, bool shrinkBoxes = false, int boxSideLength_Guess = 50)
{
std::vector<Rect> outBoxes = std::vector<Rect>();
int approxBoxSize = 1000 * boxSideLength_Guess;
while (corners.size()>4)
{
//find point above or below (these points will be removed from array after used)
int secondPtIndex = -1;
for (int i = 1; i < corners.size(); i++)
{
if (abs(corners[i].x - corners[0].x) < boxSideLength_Guess / 2.0)
{
secondPtIndex = i;
break;
}
}
if (secondPtIndex == -1)
{
std::cout << "bad box point tossed" << std::endl;
corners.erase(corners.begin() + 0);
continue;
}
//now search for closest same level point on either side
int thirdIndexRight = -1;
int thirdIndexLeft = -1;
double minDistRight = approxBoxSize;
double minDistLeft = -approxBoxSize;
for (int i = 2; i < corners.size(); i++)
{
if (abs(corners[i].y - corners[secondPtIndex].y) < boxSideLength_Guess / 2.0)
{
double dist = corners[i].x - corners[secondPtIndex].x;
if (dist < 0 && dist > minDistLeft) //check left
{
minDistLeft = dist;
thirdIndexLeft = i;
}
else if(dist > 0 && dist < minDistRight) //check right
{
minDistRight = dist;
thirdIndexRight = i;
}
}
}
if (thirdIndexLeft != -1) { approxBoxSize = 1.5 * abs(minDistLeft); }
if (thirdIndexRight != -1) { approxBoxSize = 1.5 * minDistRight; }
int fourthIndexRight = -1;
int fourthIndexLeft = -1;
for (int i = 1; i < corners.size(); i++)
{
if (i == thirdIndexLeft || i == thirdIndexRight) { continue; }
if (thirdIndexLeft != -1 && abs(corners[i].x - corners[thirdIndexLeft].x) < boxSideLength_Guess / 2.0)
{ fourthIndexLeft = i; }
if (thirdIndexRight != -1 && abs(corners[i].x - corners[thirdIndexRight].x) < boxSideLength_Guess / 2.0)
{ fourthIndexRight = i; }
}
if (!shrinkBoxes)
{
if (fourthIndexRight != -1)
{
outBoxes.push_back(Rect(corners[0], corners[thirdIndexRight]));
}
if (fourthIndexLeft != -1)
{
outBoxes.push_back(Rect(corners[0], corners[thirdIndexLeft]));
}
}
else
{
if (fourthIndexRight != -1)
{
outBoxes.push_back(Rect(corners[0] * 0.90 + corners[thirdIndexRight] *0.10, corners[0] * 0.10 + corners[thirdIndexRight] * 0.90));
}
if (fourthIndexLeft != -1)
{
outBoxes.push_back(Rect(corners[0] * 0.90 + corners[thirdIndexLeft] * 0.10, corners[0] * 0.10 + corners[thirdIndexLeft] * 0.90));
}
}
corners.erase(corners.begin() + secondPtIndex);
corners.erase(corners.begin() + 0);
}
std::cout << approxBoxSize << std::endl;
return outBoxes;
}
int main(int argc, char** argv)
{
Mat image = imread("../../resources/images/boxPic.png", CV_LOAD_IMAGE_GRAYSCALE);
imshow("source", image);
//namedWindow("Display window", WINDOW_AUTOSIZE);// Create a window for display.
//imshow("Display window", image); // Show our image inside it.
Mat edges, lineOverlay, cornerOverlay, finalBoxes;
Canny(image, edges, 50, 200, 3);
//edges = image;
//cvtColor(image, edges, COLOR_GRAY2BGR);
cvtColor(image, lineOverlay, COLOR_GRAY2BGR);
cvtColor(image, cornerOverlay, COLOR_GRAY2BGR);
cvtColor(image, finalBoxes, COLOR_GRAY2BGR);
std::cout << image.cols << " , "<<image.rows << std::endl;
std::vector<Vec2f> linesHorizontal;
std::vector<Point> ptsLH;
HoughLines(edges, linesHorizontal, 5, CV_PI / 180, 2 * edges.cols * 0.6, 0.0,0.0, CV_PI / 4, 3 * CV_PI / 4);
std::vector<Vec2f> linesVertical;
std::vector<Point> ptsLV;
HoughLines(edges, linesVertical, 5, CV_PI / 180, 2 * edges.rows * 0.6,0,0,-CV_PI/32,CV_PI/32);
for (size_t i = 0; i < linesHorizontal.size(); i++)
{
float rho = linesHorizontal[i][0], theta = linesHorizontal[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a * rho, y0 = b * rho;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
ptsLH.push_back(pt1);
ptsLH.push_back(pt2);
line(lineOverlay, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
for (size_t i = 0; i < linesVertical.size(); i++)
{
float rho = linesVertical[i][0], theta = linesVertical[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a * rho, y0 = b * rho;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
ptsLV.push_back(pt1);
ptsLV.push_back(pt2);
line(lineOverlay, pt1, pt2, Scalar(0, 255, 0), 1, LINE_AA);
}
imshow("edged", edges);
imshow("detected lines", lineOverlay);
//look for collisions
std::vector<Point2f> xPts;
for (size_t i = 0; i < linesHorizontal.size(); i++)
{
for (size_t ii = 0; ii < linesVertical.size(); ii++)
{
Point2f xPt;
bool intersectionExists = intersection(ptsLH[2 * i], ptsLH[2 * i + 1], ptsLV[2 * ii], ptsLV[2 * ii + 1], xPt);
if (intersectionExists)
{
xPts.push_back(xPt);
}
}
}
waitKey(1000);
std::vector<Point2f> boxCorners = clusterPts(xPts, 25*25);
for (int i = 0; i < boxCorners.size(); i++)
{
circle(cornerOverlay, boxCorners[i], 5, Scalar(0, 255, 0), 2);
}
imshow("detected corners", cornerOverlay);
//group make boxes for groups of points
std::vector<Rect> ocrBoxes = findBoxes(boxCorners,true);
for (int i = 0; i < ocrBoxes.size(); i++)
{
if (i % 3 == 0) { rectangle(finalBoxes, ocrBoxes[i], Scalar(255, 0, 0), 2); }
else if(i % 3 == 1) { rectangle(finalBoxes, ocrBoxes[i], Scalar(0, 255, 0), 2); }
else if (i % 3 == 2) { rectangle(finalBoxes, ocrBoxes[i], Scalar(0, 0, 255), 2); }
}
imshow("detected boxes", finalBoxes);
waitKey(0); // Wait for a keystroke in the window
return 0;
}
I am trying to convert the following python def ´preproc_img´ to opencvsharp equivalent
def compute_norm_mat(base_width, base_height):
# normalization matrix used in image pre-processing
x = np.arange(base_width)
y = np.arange(base_height)
X, Y = np.meshgrid(x, y)
X = X.flatten()
Y = Y.flatten()
A = np.array([X*0+1, X, Y]).T
A_pinv = np.linalg.pinv(A)
return A, A_pinv
def preproc_img(img, A, A_pinv):
# compute image histogram
img_flat = img.flatten()
img_hist = np.bincount(img_flat, minlength = 256)
# cumulative distribution function
cdf = img_hist.cumsum()
cdf = cdf * (2.0 / cdf[-1]) - 1.0 # normalize
# histogram equalization
img_eq = cdf[img_flat]
diff = img_eq - np.dot(A, np.dot(A_pinv, img_eq))
# after plane fitting, the mean of diff is already 0
std = np.sqrt(np.dot(diff,diff)/diff.size)
if std > 1e-6:
diff = diff/std
return diff.reshape(img.shape)
What would the equivilent counterpart be in opencv or opencvsharp? A and A_pinv is calculated using compute_norm_mat.
I am stuck at:
private void preproc_img(Mat faceMat)
{
Cv2.EqualizeHist(faceMat, faceMat);
}
I am not sure yet if this is correct, but here is my first attempt.
private Mat preproc_img(Mat faceMat)
{
MatOfFloat hist = new MatOfFloat();
Cv2.CalcHist(new[] { faceMat }, new[] { 0 }, null, hist, 1, new[] { 256 }, new float[][] { new float[] { 0, 256 } });
Mat cdf = cumsum(hist);
cdf = cdf * (2.0 / cdf.At<float>(cdf.Rows - 1, 0)) - 1.0; // normalize
var cdfidx = cdf.GetGenericIndexer<float>();
Mat img_eq = EqualizeHist(faceMat, cdfidx);
Mat diff = (img_eq.Reshape(0, 64 * 64) - (A * (A_pinv * img_eq.Reshape(0, 64 * 64))));
var std = Math.Sqrt(diff.Dot(diff) / 64 / 64);
if (std > 1e-6)
diff = diff / std;
return diff.Reshape(0, 64);
}
private static Mat EqualizeHist(Mat faceMat, Mat.Indexer<float> cdfidx)
{
var img_eq = new Mat();
faceMat.ConvertTo(img_eq, MatType.CV_32FC1);
var idx = img_eq.GetGenericIndexer<float>();
for (var r = 0; r < img_eq.Rows; r++)
{
for (var c = 0; c < img_eq.Cols; c++)
{
idx[r, c] = cdfidx[(int)idx[r, c], 0];
}
}
return img_eq;
}
private static MatOfFloat cumsum(MatOfFloat hist)
{
var cdf = hist.Clone();
var indexer = cdf.GetIndexer();
var cumsum = 0.0f;
for (var i = 0; i < hist.Rows; i++)
{
cumsum += indexer[i, 0];
indexer[i,0] = cumsum;
}
return cdf;
}
public void initializeA()
{
MatOfInt X = new MatOfInt();
MatOfInt Y = new MatOfInt();
meshgrid(new MatOfInt(new int[] {1,64 },Enumerable.Range(0,64).ToArray()), new MatOfInt(new int[] { 1, 64 }, Enumerable.Range(0, 64).ToArray()), X, Y);
X = X.Reshape(1);
Y = Y.Reshape(1);
Console.WriteLine(X);
Console.WriteLine(Y);
A = new Mat();
A.PushBack(Mat.Ones(1, X.Cols,MatType.CV_32SC1));
A.PushBack(X);
A.PushBack(Y);
A = A.T();
A_pinv = A.Clone();
A.ConvertTo(A, MatType.CV_32FC1);
Cv2.Invert(A, A_pinv,DecompTypes.SVD);
}
I have a few questions for doing optical flow projects. I use Python 2 (planning to use lasagne to use deep learning to learn optical flow), and don't know how to convert the c++ functions to that of python in visualization of the flows.
I downloaded (from http://vision.middlebury.edu/flow/data/comp/zip/other-gt-flow.zip) some image pairs where I have to estimate their optical flow, and their ground truth flow (.flo file). The problem is, when I read the .flo file into the program, it is a vectorized code. How do I view them like how they show in the webpage (http://vision.middlebury.edu/flow/data/)? I read from various sources and tried the following, but doesn't work.
In evaluating EPE (end point error) in what form should I have my prediction to be compared with the .flo file?
The code:
################################ Reading flow file ################################
f = open('flow10.flo', 'rb')
x = np.fromfile(f, np.int32, count=1) # not sure what this gives
w = np.fromfile(f, np.int32, count=1) # width
h = np.fromfile(f, np.int32, count=1) # height
print 'x %d, w %d, h %d flo file' % (x, w, h)
data = np.fromfile(f, np.float32) # vector
data_2D = np.reshape(data, newshape=(388,584,2)); # convert to x,y - flow
x = data_2D[...,0]; y = data_2D[...,1];
################################ visualising flow file ################################
mag, ang = cv2.cartToPolar(x,y)
hsv = np.zeros_like(x)
hsv = np.array([ hsv,hsv,hsv ])
hsv = np.reshape(hsv, (388,584,3)); # having rgb channel
hsv[...,1] = 255; # full green channel
hsv[...,0] = ang*180/np.pi/2 # angle in pi
hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX) # magnitude [0,255]
bgr = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
bgr = draw_hsv(data_2D)
cv2.imwrite('opticalhsv.png',bgr)
On Middlebury's page there is a zip file called flow-code (http://vision.middlebury.edu/flow/code/flow-code.zip), which provides a tool called color_flow to convert those .flo files to color images.
On the other hand, if you want to implement your own code to do the transformation, i have this piece of code (i cannot provide the original author, it has been some time) that helps you to first compute the color:
static Vec3b computeColor(float fx, float fy)
{
static bool first = true;
// relative lengths of color transitions:
// these are chosen based on perceptual similarity
// (e.g. one can distinguish more shades between red and yellow
// than between yellow and green)
const int RY = 15;
const int YG = 6;
const int GC = 4;
const int CB = 11;
const int BM = 13;
const int MR = 6;
const int NCOLS = RY + YG + GC + CB + BM + MR;
static Vec3i colorWheel[NCOLS];
if (first)
{
int k = 0;
for (int i = 0; i < RY; ++i, ++k)
colorWheel[k] = Vec3i(255, 255 * i / RY, 0);
for (int i = 0; i < YG; ++i, ++k)
colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);
for (int i = 0; i < GC; ++i, ++k)
colorWheel[k] = Vec3i(0, 255, 255 * i / GC);
for (int i = 0; i < CB; ++i, ++k)
colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);
for (int i = 0; i < BM; ++i, ++k)
colorWheel[k] = Vec3i(255 * i / BM, 0, 255);
for (int i = 0; i < MR; ++i, ++k)
colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);
first = false;
}
const float rad = sqrt(fx * fx + fy * fy);
const float a = atan2(-fy, -fx) / (float)CV_PI;
const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
const int k0 = static_cast<int>(fk);
const int k1 = (k0 + 1) % NCOLS;
const float f = fk - k0;
Vec3b pix;
for (int b = 0; b < 3; b++)
{
const float col0 = colorWheel[k0][b] / 255.f;
const float col1 = colorWheel[k1][b] / 255.f;
float col = (1 - f) * col0 + f * col1;
if (rad <= 1)
col = 1 - rad * (1 - col); // increase saturation with radius
else
col *= .75; // out of range
pix[2 - b] = static_cast<uchar>(255.f * col);
}
return pix;
}
Then it calls the above function for all the pixels:
static void drawOpticalFlow(const Mat_<Point2f>& flow, Mat& dst, float maxmotion = -1)
{
dst.create(flow.size(), CV_8UC3);
dst.setTo(Scalar::all(0));
// determine motion range:
float maxrad = maxmotion;
if (maxmotion <= 0)
{
maxrad = 1;
for (int y = 0; y < flow.rows; ++y)
{
for (int x = 0; x < flow.cols; ++x)
{
Point2f u = flow(y, x);
if (!isFlowCorrect(u))
continue;
maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
}
}
}
for (int y = 0; y < flow.rows; ++y)
{
for (int x = 0; x < flow.cols; ++x)
{
Point2f u = flow(y, x);
if (isFlowCorrect(u))
dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
}
}
}
This is for my use in OpenCV, but the code help should anyone who wants achieve something similar.
Is there way to access the contour[i][j] in python?
I am struggling to translate this c++ into python, because the data structures are different. It's being hard making comparisions
static double distanceBtwPoints(const cv::Point a, const cv::Point b)
{
double xDiff = a.x - b.x;
double yDiff = a.y - b.y;
return std::sqrt((xDiff * xDiff) + (yDiff * yDiff));
}
static int findNearestPointIndex(const cv::Point pt, const vector<Point> points)
{
int nearestpointindex = 0;
double distance;
double mindistance = 1e+9;
for ( size_t i = 0; i < points.size(); i++)
{
distance = distanceBtwPoints(pt,points[i]);
if( distance < mindistance )
{
mindistance = distance;
nearestpointindex = i;
}
}
return nearestpointindex;
}
int main( int argc, char** argv )
{
Point pt0;
int shift=0; // optional value for drawing scaled
Scalar color = Scalar(0,0,0);
char* filename = argc >= 2 ? argv[1] : (char*)"test.png";
Mat img = imread(filename);
if (img.empty())
return -1;
vector<vector<Point> > contours;
vector<Point> contour;
findContours( bw, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );
contour = contours[0];
for ( size_t i = 0; i < contours.size(); i++)
{
if( contour.size() < contours[i].size() )
contour = contours[i];
}
for ( size_t i = 0; i < contours.size(); i++)
{
if( contour != contours[i] && contours[i].size() > 10 )
{
for ( size_t j = 0; j < contours[i].size(); j++)
{
pt0 = contours[i][j];
line(src,pt0,contour[findNearestPointIndex(pt0,contour)],color,1,LINE_8,shift);
}
}
}
}
Thank you for the patient and answers.
OpenCV Python findCountours can be used like this
import cv2
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# contours = [array([[[x1, y1]], ..., [[xn, yn]]]), array([[[x1, y1]], ..., [[xn, yn]]])]
contour = contours[0] # contours[i], where i = index of the contour
# contour = [[[x1, y1]], [[x2, y2]], ..., [[xn, yn]]]
# contour[0] = [[x1, y1]]
# contour[0][0] = [x1, y1]
# contour[0][0][0] = x1
# contour[0][0][1] = y1
That's what you need
pt0 = contour[i][j][0] # that's what you need to replace pt0 = contours[i][j];
# pt0 = [x, y], where pt0[0] = x, pt0[1] = y