javacv 人脸检测_使用JavaCV进行手和手指检测

2023-11-17

javacv 人脸检测

这篇文章是Andrew Davison博士发布的有关自然用户界面(NUI)系列的一部分,内容涉及使用JavaCV从网络摄像头视频提要中检测手。

注意:本章的所有源代码都可以从http://fivedots.coe.psu.ac.th/~ad/jg/nui055/下载。

第5章的彩色斑点检测代码(可从http://fivedots.coe.psu.ac.th/~ad/jg/nui05/获得 )可以用作其他形状分析器的基础,我将在此处进行说明。通过扩展它来检测手和手指。 在图1中,我的左手戴着黑手套。 我的Handy应用程序尝试查找并标记拇指,食指,中指,无名指和小指。 在指尖和手的重心(COG)之间绘制黄线。

图1.检测左手和手指。

我使用了第5章的HSVSelector应用程序来确定黑手套的合适HSV范围。 在执行图2所示的步骤之前,Handy会加载这些范围,以获取手部的轮廓,其COG和相对于水平面的方向。

图2。找到手轮廓。

图2中的各个阶段几乎与第5章第4.1节中的ColorRectDetector.findRect()方法执行的阶段相同。但是,Handy继续进行处理,使用凸包和凸缺陷来定位并标记手中的指尖轮廓。 这些附加步骤如图3所示。

图3.查找和标记指尖。

外壳和缺陷是使用标准OpenCV操作从轮廓获得的,我将在下面进行解释。 但是,命名手指的最后一步采用了一种颇为怪异的策略,该策略假定轮廓的缺陷是针对伸出的左手。 拇指和食指基于它们相对于COG的角度位置来定位,而其他手指则根据它们相对于那些手指的位置来标识。 这个过程非常脆弱,并且很容易混淆,如图4所示。

图4.一个错误的中指。

尽管如此,该技术还是相当可靠的,通常至少可以识别拇指和食指,而与手的方向无关,这对于基本的手势处理来说应该足够了。 但是,该应用程序无法识别手势,希望它将成为下一章的主题。

Handy的类图如图5所示,其中仅列出了公共方法。

图5.方便的类图。

Handy的顶级与第5章中的BlobsDrumming应用程序的顶级并行(例如,参见第5章的图11​​),其中Handy类管理JFrame和HandPanel,显示带注释的摄像头图像。 图2和3总结的图像分析由HandDetector类执行,该类通过调用update()传递给当前的网络摄像头快照。 当HandPanel调用HandDetector.draw()时,它将绘制当前标记的指尖,COG和连接线。

1.分析网络摄像头图像

update()方法实际上是实现图2和图3的一系列调用。

// globals
private static final int IMG_SCALE = 2; 
         // scaling applied to webcam image

// HSV ranges defining the glove color
private int hueLower, hueUpper, satLower, satUpper,
            briLower, briUpper;

// OpenCV elements
private IplImage hsvImg;  // HSV version of webcam image
private IplImage imgThreshed;  // threshold for HSV settings

// hand details
private Point cogPt;       // center of gravity (COG) of contour
private int contourAxisAngle;     
   // contour's main axis angle relative to the horiz (in degrees)
private ArrayList fingerTips;


public void update(BufferedImage im)
{
  BufferedImage scaleIm = scaleImage(im, IMG_SCALE);   
      // reduce the size of the image to make processing faster

  // convert image format to HSV
  cvCvtColor(IplImage.createFrom(scaleIm), hsvImg, CV_BGR2HSV);

  // threshold image using loaded HSV settings for user's glove
  cvInRangeS(hsvImg, cvScalar(hueLower, satLower, briLower, 0),
                     cvScalar(hueUpper, satUpper, briUpper, 0),
                        imgThreshed);

  cvMorphologyEx(imgThreshed, imgThreshed, null, null,
                                             CV_MOP_OPEN, 1);
     // erosion followed by dilation on the image to remove
     // specks of white while retaining the image size

  CvSeq bigContour = findBiggestContour(imgThreshed);
  if (bigContour == null)
    return;

  extractContourInfo(bigContour, IMG_SCALE);
     // find the COG and angle to horizontal of the contour

  findFingerTips(bigContour, IMG_SCALE);
      // detect the fingertips positions in the contour

  nameFingers(cogPt, contourAxisAngle, fingerTips);
}  // end of update()

update()首先缩放提供的网络摄像头图像以提高处理速度。 然后,它将图片转换为HSV格式,以便可以使用黑手套的HSV范围生成阈值图像。 这对应于图2的第一行,尽管实际上将阈值渲染为黑色背景上的白色像素。

减去小斑点的阈值传递给findBiggestContour(); 在随后的处理阶段中,假定生成的轮廓是用户的手。 extractContourInfo()分析轮廓以找到手的重心(COG)及其相对于水平面的方向,并将其存储在cogPt和ContourAxisAngle全局变量中。 extractContourInfo()的完成对应于图2的末尾。

findFingerTips()方法将凸包包裹在轮廓周围,以识别形状的缺陷(图3的顶行),我们假设这是手的手指。 经过少量过滤以减少缺陷数量之后,其余缺陷将被视为指尖坐标,并存储在全局fingerTips列表中。

nameFingers()标记手指(假设拇指和食指在手的左侧),完成图3的阶段。

1.1找到最大的轮廓

findBiggestContour()使用OpenCV函数cvFindContours()创建轮廓列表。 对于我的二进制阈值图像,轮廓是白色像素的区域(或斑点)。 每个斑点由一个边界框近似,并且选择并返回与最大框相对应的轮廓。

// globals
private static final float SMALLEST_AREA = 600.0f;
            // ignore smaller contour areas

private CvMemStorage contourStorage;


private CvSeq findBiggestContour(IplImage imgThreshed)
{
  CvSeq bigContour = null;
  // generate all the contours in the threshold image as a list
  CvSeq contours = new CvSeq(null);
  cvFindContours(imgThreshed, contourStorage, contours,
                          Loader.sizeof(CvContour.class),
                          CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

  // find the largest contour in the list based on bounded box size
  float maxArea = SMALLEST_AREA;
  CvBox2D maxBox = null;
  while (contours != null && !contours.isNull()) {
    if (contours.elem_size() > 0) {
      CvBox2D box = cvMinAreaRect2(contours, contourStorage);
      if (box != null) {
        CvSize2D32f size = box.size();
        float area = size.width() * size.height();
        if (area > maxArea) {
          maxArea = area;
          bigContour = contours;
        }
      }
    }
    contours = contours.h_next();
  }
  return bigContour;
}  // end of findBiggestContour()

cvFindContours()可以返回不同类型的轮廓,这些轮廓收集在不同类型的数据结构中。 我生成最简单的轮廓,将它们存储在线性列表中,可以使用while循环进行搜索。

经过一些实验后,我在600平方像素的有边界框上放置了一个下限,以滤除围绕图像噪点的小框。 这意味着,如果findBiggestContour()找不到足够大的框,则可能返回null。

1.2计算COG和水平角

图2所示的下一步是通过调用extractContourInfo()查找COG和与手部轮廓水平线的夹角。 在此,HandDetector中的代码从ColorRectDetector.findRect()在第5章中进行的分析开始成为公司的一部分。在该章的4.2节中,轮廓周围的封闭框用于获得中心和方向。 这是足够的,因为基础形状是矩形卡片,因此轮廓和框几乎相同。 但是,手周围的边界框可能很容易与手本身具有不同的COG或角度。 在这种情况下,有必要利用力矩直接分析手部轮廓而不是边界框。

我在第3章中使用了空间矩来查找二进制图像的COG。 可以将相同的技术应用于轮廓以找到其中心(或质心)。 我还可以计算二阶混合矩,它提供了有关质心周围像素散布的信息。 可以组合二阶矩以返回轮廓的主轴相对于x轴的方向(或角度)。

回顾第三章的OpenCV矩符号,m()矩函数定义为:

该函数带有两个参数p和q,它们用作x和y的幂。 I()函数是由像素的(x,y)坐标定义的像素的强度。 n是组成形状的像素数。

如果考虑图6中的轮廓,则θ是其主轴线与水平面的角度,+ y轴指向下方。

图6.轮廓线及其主轴线。

就m()函数而言,可以证明:

如下所示的extractContourInfo()方法使用空间矩获取轮廓的质心,并使用cvGetCentralMoment()根据上述公式计算主轴角度; 这些结果存储在全局变量cogPt和ContourAxisAngle中,以备后用。

// globals
private Point cogPt;       // center of gravity (COG) of contour
private int contourAxisAngle;     
     // contour's main axis angle relative to horizontal (in degrees)

private ArrayList fingerTips;


private void extractContourInfo(CvSeq bigContour, int scale)
{
  CvMoments moments = new CvMoments();
  cvMoments(bigContour, moments, 1);

  // center of gravity
  double m00 = cvGetSpatialMoment(moments, 0, 0) ;
  double m10 = cvGetSpatialMoment(moments, 1, 0) ;
  double m01 = cvGetSpatialMoment(moments, 0, 1);

  if (m00 != 0) {   // calculate center
    int xCenter = (int) Math.round(m10/m00)*scale;
    int yCenter = (int) Math.round(m01/m00)*scale;
    cogPt.setLocation(xCenter, yCenter);
  }

  double m11 = cvGetCentralMoment(moments, 1, 1);
  double m20 = cvGetCentralMoment(moments, 2, 0);
  double m02 = cvGetCentralMoment(moments, 0, 2);
  contourAxisAngle = calculateTilt(m11, m20, m02);

  // deal with hand contour pointing downwards
  /* uses fingertips information generated on the last update of
     the hand, so will be out-of-date */

  if (fingerTips.size() > 0) {
    int yTotal = 0;
    for(Point pt : fingerTips)
      yTotal += pt.y;
    int avgYFinger = yTotal/fingerTips.size();
    if (avgYFinger > cogPt.y)   // fingers below COG
      contourAxisAngle += 180;
  }
  contourAxisAngle = 180 - contourAxisAngle;  
       /* this makes the angle relative to a positive y-axis that
          runs up the screen */
}  // end of extractContourInfo()



private int calculateTilt(double m11, double m20, double m02)
{
  double diff = m20 - m02;
  if (diff == 0) {
    if (m11 == 0)
      return 0;
    else if (m11 > 0)
      return 45;
    else   // m11 < 0
      return -45;
  }

  double theta = 0.5 * Math.atan2(2*m11, diff);
  int tilt = (int) Math.round( Math.toDegrees(theta));

  if ((diff > 0) && (m11 == 0))
    return 0;
  else if ((diff < 0) && (m11 == 0))
    return -90;
  else if ((diff > 0) && (m11 > 0))  // 0 to 45 degrees
    return tilt;
  else if ((diff > 0) && (m11 < 0))  // -45 to 0
    return (180 + tilt);   // change to counter-clockwise angle
  else if ((diff < 0) && (m11 > 0))   // 45 to 90
    return tilt;
  else if ((diff < 0) && (m11 < 0))   // -90 to -45
    return (180 + tilt);  // change to counter-clockwise angle

  System.out.println("Error in moments for tilt angle");
  return 0;
}  // end of calculateTilt()

Johannes Kilian在http://public.cranfield.ac.uk/c5354/teaching/dip/opencv/SimpleImageAnalysisbyMoments.pdf上的 “通过矩的简单图像分析”中对OpenCV的矩进行了详细说明。 calculateTilt()内的代码基于Kilian论文表1中列出的θ特殊情况。

不幸的是,轴角无法区分手指指向上方的手和手指指向下方的手,因此有必要检查指尖相对于COG的相对位置,以确定是否应调整角度。 问题在于,只有在检查了手部轮廓的凸包是否存在缺陷之后(在extractContourInfo()完成之后才出现此缺陷),该信息才可用。

我的解决方案是使用在上次调用update()时计算出的指尖坐标,该指针分析了当前帧之前的摄像头帧。 数据将是过时的,但是在两次捕捉之间的200 ms间隔内指针不会移动太多。

1.3找到指尖

指尖的识别在图3的第一行中进行; 在代码中,通过OpenCV的cvConvexHull2()将凸包包裹在轮廓上,并通过cvConvexityDefects()将多边形与轮廓进行比较以查找其缺陷。

利用轮廓的低多边形近似值而不是原始值,可以加快船体创建和缺陷分析的速度。

这些阶段在findFingerTips()方法的前半部分执行:

// globals
private static final int MAX_POINTS = 20;  
               // max number of points stored in an array

// OpenCV elements
private CvMemStorage contourStorage, approxStorage,
                     hullStorage, defectsStorage;

// defects data for the hand contour
private Point[] tipPts, foldPts;   
private float[] depths;


private void findFingerTips(CvSeq bigContour, int scale)
{
  CvSeq approxContour = cvApproxPoly(bigContour,
                 Loader.sizeof(CvContour.class),
                 approxStorage, CV_POLY_APPROX_DP, 3, 1);
     // reduce number of points in the contour

  CvSeq hullSeq = cvConvexHull2(approxContour,
                    hullStorage, CV_COUNTER_CLOCKWISE, 0);
     // find the convex hull around the contour

  CvSeq defects = cvConvexityDefects(approxContour,
                                 hullSeq, defectsStorage);
     // find the defect differences between the contour and hull

  int defectsTotal = defects.total();
  if (defectsTotal > MAX_POINTS) {
    System.out.println("Processing " + MAX_POINTS + " defect pts");
    defectsTotal = MAX_POINTS;
  }

  // copy defect information from defects sequence into arrays
  for (int i = 0; i < defectsTotal; i++) {
    Pointer pntr = cvGetSeqElem(defects, i);
    CvConvexityDefect cdf = new CvConvexityDefect(pntr);

    CvPoint startPt = cdf.start();
    tipPts[i] = new Point( (int)Math.round(startPt.x()*scale),
                              (int)Math.round(startPt.y()*scale));
      // array contains coords of the fingertips

    CvPoint endPt = cdf.end();
    CvPoint depthPt = cdf.depth_point();
    foldPts[i] = new Point( (int)Math.round(depthPt.x()*scale),
                              (int)Math.round(depthPt.y()*scale));
        //array contains coords of the skin fold between fingers

    depths[i] = cdf.depth()*scale;
        // array contains distances from tips to folds
  }

  reduceTips(defectsTotal, tipPts, foldPts, depths);
}  // end of findFingerTips()

findFingerTips()的后半部分从缺陷序列中提取尖端和褶皱坐标以及深度。 先前使用CV_COUNTER_CLOCKWISE参数调用凸包方法cvConvexHull2()意味着将以逆时针顺序存储坐标,如图7所示。

图7.指尖,褶皱和深度。

指尖存储在tipPts []数组中,手指在foldPts []中折叠(手指之间的凹痕),深度在depths []中。

如图7所示,分析通常会产生太多缺陷,因此在findFingerTips()的末尾会调用reduceTips()。 它应用了两个简单的测试来滤除不太可能是指尖的缺陷-丢弃缺陷深度较浅的点,并在其相邻折叠点之间以太大的角度进行坐标。 两者的示例如图8所示。

图8.浅深度和广角。

reduceTips()将其余的提示点存储在全局fingerTips列表中:

// globals
private static final int MIN_FINGER_DEPTH = 20;
private static final int MAX_FINGER_ANGLE = 60;   // degrees

private ArrayList fingerTips;


private void reduceTips(int numPoints, Point[] tipPts,
                     Point[] foldPts, float[] depths)
{
  fingerTips.clear();

  for (int i=0; i < numPoints; i++) {
    if (depths[i] < MIN_FINGER_DEPTH)    // defect too shallow
      continue;

    // look at fold points on either side of a tip
    int pdx = (i == 0) ? (numPoints-1) : (i - 1); // predecessor of i
    int sdx = (i == numPoints-1) ? 0 : (i + 1);   // successor of i

    int angle = angleBetween(tipPts[i], foldPts[pdx], foldPts[sdx]);
    if (angle >= MAX_FINGER_ANGLE)    
      continue;      // angle between finger and folds too wide

    // this point is probably a fingertip, so add to list
    fingerTips.add(tipPts[i]);
  }
}  // end of reduceTips()


private int angleBetween(Point tip, Point next, Point prev)
// calculate the angle between the tip and its neighboring folds
// (in integer degrees)
{
  return Math.abs( (int)Math.round(
            Math.toDegrees(
                  Math.atan2(next.x - tip.x, next.y - tip.y) -
                  Math.atan2(prev.x - tip.x, prev.y - tip.y)) ));
}

1.4命名手指

nameFingers()使用指尖坐标列表以及轮廓的COG和轴角来分两步标记手指。 首先,它会基于它们相对于COG的可能角度调用labelThumbIndex()来标记拇指和食指,假设它们位于手的左侧。 nameFingers()会根据相对于拇指和食指的已知顺序,尝试在labelUnknowns()中标记其他手指。

// globals
private ArrayList namedFingers;


private void nameFingers(Point cogPt, int contourAxisAngle,
                                   ArrayList fingerTips)
{ // reset all named fingers to unknown
  namedFingers.clear();
  for (int i=0; i < fingerTips.size(); i++)
    namedFingers.add(FingerName.UNKNOWN);

  labelThumbIndex(fingerTips, namedFingers);
  labelUnknowns(namedFingers);
}  // end of nameFingers()

Finger ID及其相对顺序在FingerName枚举中维护:

public enum FingerName {
   LITTLE, RING, MIDDLE, INDEX, THUMB, UNKNOWN;


  public FingerName getNext()
  { 
    int nextIdx = ordinal()+1;
    if (nextIdx == (values().length))
      nextIdx = 0;
    return values()[nextIdx]; 
  }  // end of getNext()


  public FingerName getPrev()
  { 
    int prevIdx = ordinal()-1;
    if (prevIdx < 0)
      prevIdx = values().length-1;
    return values()[prevIdx]; 
  }  // end of getPrev()

}  // end of FingerName enum

可能的手指名称之一是UNKNOWN,该名称用于在调用命名方法之前标记所有指尖。

labelThumbIndex()尝试根据图9中所示的角度范围来标记拇指和食指。

图9.拇指和食指的角度范围。

食指可以围绕COG旋转60至120度,而拇指可以在120至200度之间移动。 我通过反复试验得出了这些角度,他们认为手是笔直向上的。

labelThumbIndex()还假定拇指和食指最有可能存储在fingerTips列表的末尾,因为轮廓船体是按逆时针顺序构建的。 因此,通过向后遍历列表,可以增加与正确缺陷匹配的机会。

// globals
private static final int MIN_THUMB = 120;  // angle ranges
private static final int MAX_THUMB = 200;

private static final int MIN_INDEX = 60;
private static final int MAX_INDEX = 120;

// hand details
private Point cogPt
private int contourAxisAngle;     


private void labelThumbIndex(ArrayList fingerTips,
                             ArrayList nms)
{
  boolean foundThumb = false;
  boolean foundIndex = false;
  int i = fingerTips.size()-1;
  while ((i >= 0)) {
    int angle = angleToCOG(fingerTips.get(i),
                               cogPt, contourAxisAngle);
    // check for thumb
    if ((angle <= MAX_THUMB) && (angle>MIN_THUMB) && !foundThumb) {
      nms.set(i, FingerName.THUMB);
      foundThumb = true;
    }

    // check for index
    if ((angle <= MAX_INDEX) && (angle > MIN_INDEX) && !foundIndex) {
      nms.set(i, FingerName.INDEX);
       foundIndex = true;
    }
    i--;
  }
}  // end of labelThumbIndex()

angleToCOG()计算指尖相对于COG的角度,记住要记住轮廓轴角度,以便使手笔直向上。

private int angleToCOG(Point tipPt, Point cogPt,
                                     int contourAxisAngle)
{
  int yOffset = cogPt.y - tipPt.y;    // make y positive up screen
  int xOffset = tipPt.x - cogPt.x;
  double theta = Math.atan2(yOffset, xOffset);
  int angleTip = (int) Math.round( Math.toDegrees(theta));
  return angleTip + (90 - contourAxisAngle);
           // this ensures that the hand is orientated straight up
}  // end of angleToCOG()

labelUnknowns()传递了一个手指名称列表,该列表希望在某些位置包含THUMB和INDEX,而在其他位置包含UNKNOWN。 使用命名的手指作为起点,根据手指在FingerName枚举中的顺序,将UNKNOWN更改为手指名称。

private void labelUnknowns(ArrayList nms)
{
  // find first named finger
  int i = 0;
  while ((i < nms.size()) && (nms.get(i) == FingerName.UNKNOWN))
    i++;
  if (i == nms.size())   // no named fingers found, so give up
    return;

  FingerName name = nms.get(i);
  labelPrev(nms, i, name);    // fill-in backwards
  labelFwd(nms, i, name);     // fill-in forwards
}  // end of labelUnknowns()

labelPrev()和labelFwd()的区别仅在于它们在名称列表中移动的方向。 labelPrev()向后移动以尝试将UNKNOWNS更改为已命名的手指,但前提是尚未将名称分配给列表。

2.画出手指

由update()执行的分析将得到指尖点列表(在fingertips全局中),关联的已命名手指列表(在namedFingers中)以及轮廓COG和轴角度。 除角度外,所有这些都由draw()用来将命名的手指标签添加到网络摄像头图像中,如下图1和图10所示。

图10.命名的手指和未知的手指。

未知的手指“尖端”(在namedFingers中标记为UNKNOWN)被绘制为红色圆圈。

// globals
private Point cogPt;
private ArrayList fingerTips;
private ArrayList namedFingers;


public void draw(Graphics2D g2d)
{
  if (fingerTips.size() == 0)
    return;

  g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
          RenderingHints.VALUE_ANTIALIAS_ON);  // line smoothing
  g2d.setPaint(Color.YELLOW);
  g2d.setStroke(new BasicStroke(4));  // thick yellow pen

  // label tips in red or green, and draw lines to named tips
  g2d.setFont(msgFont);
  for (int i=0; i < fingerTips.size(); i++) {
    Point pt = fingerTips.get(i);
    if (namedFingers.get(i) == FingerName.UNKNOWN) {
      g2d.setPaint(Color.RED);   // unnamed fingertip is red
      g2d.drawOval(pt.x-8, pt.y-8, 16, 16);
      g2d.drawString("" + i, pt.x, pt.y-10);   // label with a digit
    }
    else {   // draw yellow line to the named fingertip from COG
      g2d.setPaint(Color.YELLOW);
      g2d.drawLine(cogPt.x, cogPt.y, pt.x, pt.y);

      g2d.setPaint(Color.GREEN);   // named fingertip is green
      g2d.drawOval(pt.x-8, pt.y-8, 16, 16);
      g2d.drawString(namedFingers.get(i).toString().toLowerCase(),
                            pt.x, pt.y-10);
    }
  }

  // draw COG
  g2d.setPaint(Color.GREEN);
  g2d.fillOval(cogPt.x-8, cogPt.y-8, 16, 16);
}  // end of draw()

3.手势检测

Handy应用程序会尽力将已命名的指尖转换为手势,这需要分析手指随着时间在空间中的移动方式。

初步测试表明,Handy仅在涉及伸出的拇指和/或食指(也许与其他手指结合在一起)时,才能可靠地识别手势。 这种手势包括图11中所示的“胜利”,“波浪”,“良好”,“指向”和“枪支”。

图11.适用于便捷式检测的手势。

Handy无法检测到的常见手势是“ ok”(参见图12),因为它需要将手指放在一起,而这不能仅根据轮廓缺陷来检测到。

图12.不合适的“确定”手势。

参考: Java Advent Calendar博客上的JCG合作伙伴 Attila-Mihaly Balazs 使用JavaCV进行的手和手指检测

翻译自: https://www.javacodegeeks.com/2012/12/hand-and-finger-detection-using-javacv.html

javacv 人脸检测

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

javacv 人脸检测_使用JavaCV进行手和手指检测 的相关文章

  • 数组到集合:优化代码

    有更好的方法来实现这一目标吗 public static List
  • 姜戈。列出静态文件夹中的文件

    我遇到的一件看似基本的事情是将一个简单的静态文件列表 例如我的服务器上单个存储库目录的内容 呈现为链接列表 这是否安全是另一个问题 但假设我想这样做 这就是我的工作目录的样子 我想将模板中分析文件夹的所有文件作为链接列出 I have tr
  • 构造函数重载相同的参数

    假设我的类有 2 个字段 x 和 y 类型为double 是否可以定义 2 个构造函数 以便 constructor1 将创建对象并设置其x构造函数中参数的属性告诉和y默认值和构造函数2反之亦然 public class Test priv
  • java rmi 身份验证和安全。 exportObject 使其公开?

    问题 当你UnicastRemoteObject exportObject instance 该实例现在是否可供所有客户公开使用 即使找到它的端口需要一点技巧 情况是这样的 我有一个 java RMI 客户端 服务器设置 我想添加一些身份验
  • Tweepy API v2 身份验证

    如何使用 API v2 测试 Tweepy 身份验证 我的下面的代码由于身份验证而无法发布 但是 是否有其他方法可以在不调用发布推文 client create tweet 的情况下测试身份验证 import tweepy bearer t
  • 如何在Spyder(Anaconda 3)上安装PIL?

    这是我使用 conda install PIL 继续安装时出现的问题 它给了我这个 UnsatisfiableError The following specifications were found to be in conflict p
  • 本地化:django-admin compilemessages 跳过 venv

    我正在使用本地化Django 1 11应用 我可以排除虚拟环境文件夹node modules文件夹 同时使用消息文件添加消息 i选项如 django admin makemessages l no i venv django admin m
  • Javascript 与 Python 关于 Python 'map()' 函数的比较

    Python中有一个函数叫做map这可以让你去 map someFunction x y z 并继续应用该功能的列表 是否有与此功能等效的 JavaScript 我现在刚刚学习Python 虽然我被告知javascript是函数式语言 但我
  • Python 应用程序的最佳项目结构是什么? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 想象一下 您想要用 Python 开发一个重要的最终用户桌面 不是 Web 应用程序 构建项目文件夹层次结构的最佳方法是什么 理想的特
  • Grizzly 和 Servlet 容器上下文

    我试图在我编写的 在 Grizzly 上运行的 Servlet 中获取一些注入的上下文 例如 Session 或 HttpServletRequest 但我所做的似乎都不起作用 整个过程似乎过早地停止了 并出现以下错误 SEVERE Mis
  • python导入模块时如何避免一直写模块名?

    我用math最近模块很多 我不想写math sqrt x and math sin x 每时每刻 我想缩短它并写sqrt x and sin x How 对于较长的模块名称 通常会缩短它们 例如 import numpy as np 然后您
  • 光学标记阅读器的开源库[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我想要一个用于光学标记读取 OMR 的开源库 有这样的库吗 我想在我的 java 应用程序中使用这个库 zxing 可能对你有用 http
  • 为什么我不能使用“exclude”从 python 轮子中排除“tests”目录?

    考虑以下包结构 与以下setup py内容 from setuptools import setup find packages setup name dfl client packages find packages exclude te
  • 在 Java 中跨平台地播种随机生成器,无需时间

    我几乎同时在两个线程上初始化两个随机数生成器 并且我希望这两个生成器的行为完全不同 我会打电话Random nextInt 7 经常一个接一个地在两台发电机上运行 使用System currentTimeMillis 这不是一个好主意 因为
  • DataFrame 在函数内部修改

    我面临一个我以前从未观察到的函数内数据帧修改的问题 有没有一种方法可以处理这个问题 以便初始数据帧不被修改 def test df df tt np nan return df dff pd DataFrame data 现在 当我打印时d
  • 无法让远程 EJB 与 Wildfly 上的 EJB 客户端 API 配合使用

    我目前正在努力让远程 EJB 调用在 wildfly 8 x 和 9 x 上工作 详细来说 它是关于使用 EJB 客户端 API 方法从独立客户端应用程序 而不是从另一个应用程序服务器 进行远程调用 远程命名方法适用于我 但不适用于我的场景
  • 子进程改变目录

    我想在子目录 超级目录中执行脚本 我需要首先进入该子目录 超级目录 我无法得到subprocess进入我的子目录 tducin localhost Projekty tests ve python Python 2 7 4 default
  • Python 中的安全解除引用

    Groovy 有一个很好的安全取消引用运算符 这有助于避免 NullPointerExceptions variable method The method仅当以下情况时才会被调用variable is not null 有没有办法在 Py
  • Scikit-learn 的内核 PCA:如何在 KPCA 中实现各向异性高斯内核或任何其他自定义内核?

    我目前正在使用Scikit learn 的 KPCA https scikit learn org stable modules generated sklearn decomposition KernelPCA html对我的数据集执行降
  • 寻找 WebElements,最佳实践

    在我们当前的自动化 使用 Selenium WebDriver Java 中 我们使用 FindBy very广泛地 例如 FindBy css a name bcrumb protected List

随机推荐

  • C++ Web服务器 - 用户登录(三)

    newobj跨平台开发框架 https github com Liuccysdgg newobj 本片着重介绍 MYSQL连接池 HTTP静态文件响应 部分JS等 效果演示 一 MYSQL连接池 如果每次业务请求进来时去创建mysql连接并
  • Node Sass does not yet support your current environment: Windows 64-bit with Unsupported runtime问题解决

    Node Sass does not yet support your current environment Windows 64 bit with Unsupported runtime问题解决 运行原先vue程序时 npm run d
  • Typescript、VUE3的相关介绍

    一 Typescript 1 TypeScript 的由来 TypeScript 是由微软开发的一款开源的编程语言 TypeScript 是 Javascript 的超集 遵循最新的 ES6 ES5 规范 TypeScript 扩展了 Ja
  • 动态规划问题——最长上升子序列(LIS)(一)

    原文转载自我的博客benym cn 推荐链接 动态规划问题 最长上升子序列 LIS 二 动态规划问题 最长上升子序列 LIS 三 如 求 2 7 1 5 6 4 3 8 9 的最长上升子序列 我们定义d i i 1 n 来表示前i个数以A
  • chatgpt赋能Python-python2_7如何安装

    Python 2 7如何安装 Python 2 7是一个广泛使用的Python版本 其可以在Windows和Linux上安装 本文将介绍Python 2 7如何安装 并提供相关步骤和指南 下载Python 2 7 首先 您需要下载Pytho
  • Python for循环嵌套

    视频版教程 Python3零基础7天入门实战视频教程 在有复杂应用的时候 我们可以通过for循环的嵌套来实现 比如打印二维的行列 这里先学习下range 方法 获取一个数字序列 案例 range stop 返回0到stop 1的数字序列 f
  • SQL 通配符

    在 SQL 中 通配符与 SQL LIKE 操作符一起使用 SQL 通配符用于搜索表中的数据 在 SQL 中 可使用以下通配符 通配符 描述 替代 0 个或多个字符 替代一个字符 charlist 字符列中的任何单一字符 charlist
  • Angular项目配置本地https访问

    Angular项目配置本地https访问 首先 先创建一个项目 d work learn ng new angular https 然后cd到刚生成的项目的根目录 建立一个cert目录 用于存放我们的密钥证书等文件 cd angular h
  • 转载---贪心算法

    转载博主 1 贪心算法简介 1 1 基本定义 在贪婪算法 greedy method 中 我们要逐步构造一个最优解 每一步 我们都在一定的标准下 做出一个最优决策 做出决策所依据的标准称为贪心准则 greedy criterion 贪心算法
  • python语法-类型注解

    python语法 类型注解 类型注解 在代码中涉及数据交互的地方 提供数据类型的注解 显式说明 主要功能 帮助第三方IDE工具 如pycharm 对代码进行类型推断 协助做代码提示 帮助开发者自身对变量进行类型注释 支持 变量的类型注解 函
  • 如何安装wsl以及安装时遇到的一些问题

    首先我安装wsl是参考这篇文章 Windows安装WSL详解 讲得挺具体 如果安装过程一切顺利就可以跳过下面的内容了 wsl在终端安装遇到的问题 当我在终端输入wsl install 返回了一列数据 但显然不是安装成功 于是我输入wsl i
  • document onload和window onload的区别?

    答 Document onload 是在结构和样式加载完成才执行的JS Window onload 不仅仅要在结构和样式加载完 还要执行完所有的样式 图片这些资源文件 完全加载完才会触发Window onload事件
  • Oracle SQL查询之Like使用正则表达式

    select from si0001 temp a where regexp like a xm a z A Z 查询xm字段带有英文的数据
  • 2019年第十届蓝桥杯国赛B组试题G-排列数-next_permutation枚举,模拟

    在一个排列中 一个折点是指排列中的一个元素 它同时小于两边的元素 或者同时大于两边的元素 对于一个 1 n 的排列 如果可以将这个排列中包含 t个折点 则它称为一个 t 1 单调序列 例如 排列 1 4 2 3 是一个 3 单调序列 其中
  • Mapreduce学习基础

    一 Mapreduce的基础 1 1 为什么要学习Mapreduce 1 单机资源受限 比如CPU 磁盘 2 分布式计算的程序的复杂度特别高 难度大 mapreduce就是解决以上问题的 1 利用集群的所有cpu 所有内存 所有磁盘 2 m
  • 【TCP/IP】第四章 IP协议

    4 1 即网际协议 TCP IP的心脏是互联网层 这一层主要是由IP Internet Protocol 和ICMP Internet Control Message Protocol 两个协议组成 IP相当于OSI参考模型的第3层 IP
  • 06-TensorFlow 自制数据集

    1 数据文件介绍 数据集下载 https download csdn net download qq 41865229 85254826 训练集60000张数字图片 测试集10000张图片 都是黑底白字的灰度图 每张图有28行28列个像素点
  • c#下各种数据库操作的封装!(支持ACCESS,SQLSERVER,DB2,ORACLE,MYSQL)

    首先定义数据库操作的标准接口IDBAccess 定义接口的基本功能 通过基本的接口设置 完成数据访问的统一抽象 public interface IDBAccess void Init string strServer string str
  • android编译时报错:Error parsing XML: unbound prefix 的错误原因及解决方案

    原因之一 拼写错误 例如 android写成androd之类 原因之二 xmlns没有添加 有的时候 自定了一些view 且新加了一些自定义的命名 那么需要添加声明到根上 如果忘了添加也会报这个错误 xmlns calendar http
  • javacv 人脸检测_使用JavaCV进行手和手指检测

    javacv 人脸检测 这篇文章是Andrew Davison博士发布的有关自然用户界面 NUI 系列的一部分 内容涉及使用JavaCV从网络摄像头视频提要中检测手 注意 本章的所有源代码都可以从http fivedots coe psu