iOS計(jì)算機(jī)視覺—人臉識(shí)別

簡介

引自維基百科計(jì)算機(jī)視覺是一門研究如何使機(jī)器“看”的科學(xué),更進(jìn)一步的說,就是指用攝影機(jī)和計(jì)算機(jī)代替人眼對(duì)目標(biāo)進(jìn)行識(shí)別、跟蹤和測量等機(jī)器視覺,并進(jìn)一步做圖像處理,用計(jì)算機(jī)處理成為更適合人眼觀察或傳送給儀器檢測的圖像。

計(jì)算機(jī)視覺系統(tǒng)的結(jié)構(gòu)形式很大程度上依賴于其具體應(yīng)用方向。計(jì)算機(jī)視覺系統(tǒng)的具體實(shí)現(xiàn)方法同時(shí)也由其功能決定——是預(yù)先固定的抑或是在運(yùn)行過程中自動(dòng)學(xué)習(xí)調(diào)整。盡管如此,計(jì)算機(jī)視覺的都需要具備以下處理步驟:

引自維基百科人臉識(shí)別,特指利用分析比較人臉視覺特征信息進(jìn)行身份鑒別的計(jì)算機(jī)技術(shù)。
廣義的人臉識(shí)別實(shí)際包括構(gòu)建人臉識(shí)別系統(tǒng)的一系列相關(guān)技術(shù),包括人臉圖像采集、人臉定位、人臉識(shí)別預(yù)處理、身份確認(rèn)以及身份查找等;而狹義的人臉識(shí)別特指通過人臉進(jìn)行身份確認(rèn)或者身份查找的技術(shù)或系統(tǒng)。

人臉識(shí)別是計(jì)算機(jī)視覺的一種應(yīng)用,iOS中常用的有四種實(shí)現(xiàn)方式:CoreImage、Vision、OpenCV、AVFoundation,下面一一介紹幾種方式的實(shí)現(xiàn)步驟。

CoreImage

自從 iOS 5(大概在2011年左右)之后,iOS 開始支持人臉識(shí)別,只是用的人不多。人臉識(shí)別 API 讓開發(fā)者不僅可以進(jìn)行人臉檢測,還能識(shí)別微笑、眨眼等表情。

操作部分:

  • 濾鏡(CIFliter):CIFilter 產(chǎn)生一個(gè)CIImage。典型的,接受一到多的圖片作為輸入,經(jīng)過一些過濾操作,產(chǎn)生指定輸出的圖片。
  • 檢測(CIDetector):CIDetector 檢測處理圖片的特性,如使用來檢測圖片中人臉的眼睛、嘴巴、等等。
  • 特征(CIFeature):CIFeature 代表由 detector處理后產(chǎn)生的特征。

圖像部分:

  • 畫布(CIContext):畫布類可被用與處理Quartz 2D 或者 OpenGL。可以用它來關(guān)聯(lián)CoreImage類。如濾鏡、顏色等渲染處理。
  • 顏色(CIColor): 圖片的關(guān)聯(lián)與畫布、圖片像素顏色的處理。
  • 向量(CIVector): 圖片的坐標(biāo)向量等幾何方法處理。
  • 圖片(CIImage): 代表一個(gè)圖像,可代表關(guān)聯(lián)后輸出的圖像。

實(shí)現(xiàn)代碼如下:

        guard let personciImage = CIImage(image: personPic.image!) else { return }
        let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
        let faces = faceDetector?.features(in: personciImage)
        
        // 轉(zhuǎn)換坐標(biāo)系
        let ciImageSize = personciImage.extent.size
        var transform = CGAffineTransform(scaleX: 1, y: -1)
        transform = transform.translatedBy(x: 0, y: -ciImageSize.height)
        
        for face in faces as! [CIFaceFeature] {
            print("Found bounds are \(face.bounds)")
            // 應(yīng)用變換轉(zhuǎn)換坐標(biāo)
            var faceViewBounds = face.bounds.applying(transform)
            // 在圖像視圖中計(jì)算矩形的實(shí)際位置和大小
            let viewSize = personPic.bounds.size
            let scale = min(viewSize.width / ciImageSize.width, viewSize.height / ciImageSize.height)
            let offsetX = (viewSize.width - ciImageSize.width * scale) / 2
            let offsetY = (viewSize.height - ciImageSize.height * scale) / 2
            
            faceViewBounds = faceViewBounds.applying(CGAffineTransform(scaleX: scale, y: scale))
            faceViewBounds.origin.x += offsetX
            faceViewBounds.origin.y += offsetY
            
            let faceBox = UIView(frame: faceViewBounds)
            faceBox.layer.borderWidth = 3
            faceBox.layer.borderColor = UIColor.red.cgColor
            faceBox.backgroundColor = UIColor.clear
            personPic.addSubview(faceBox)
            
            if face.hasLeftEyePosition {
                print("Left eye bounds are \(face.leftEyePosition)")
            }
            
            if face.hasRightEyePosition {
                print("Right eye bounds are \(face.rightEyePosition)")
            }
        }

運(yùn)行APP,效果如下:

Vision

Vision 是 Apple 在 WWDC 2017 推出的圖像識(shí)別框架,它基于 Core ML,所以可以理解成 Apple 的工程師設(shè)計(jì)了一種算法模型,然后利用 Core ML 訓(xùn)練,最后整合成一個(gè)新的框架,相比開源模型然后讓開發(fā)者自己整合起來,這種方式更安全也更方便我們使用。

Vision 支持多種圖片類型,如:

  • CIImage
  • NSURL
  • NSData
  • CGImageRef
  • CVPixelBufferRef

Vision使用中的角色有:
Request,RequestHandler,results和results中的Observation數(shù)組。

Request類型:
有很多種,比如圖中列出的 人臉識(shí)別、特征識(shí)別、文本識(shí)別、二維碼識(shí)別等。

使用概述:

我們?cè)谑褂眠^程中是給各種功能的 Request 提供給一個(gè) RequestHandler,Handler 持有需要識(shí)別的圖片信息,并將處理結(jié)果分發(fā)給每個(gè) Request 的 completion Block 中。可以從 results 屬性中得到 Observation 數(shù)組。

observations數(shù)組中的內(nèi)容根據(jù)不同的request請(qǐng)求返回了不同的observation,如:VNFaceObservation、VNTextObservation、VNBarcodeObservation、VNHorizonObservation,不同的Observation都繼承于VNDetectedObjectObservation,而VNDetectedObjectObservation則是繼承于VNObservation。每種Observation有boundingBox,landmarks等屬性,存儲(chǔ)的是識(shí)別后物體的坐標(biāo),點(diǎn)位等,我們拿到坐標(biāo)后,就可以進(jìn)行一些UI繪制。

代碼實(shí)現(xiàn)如下:

    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let handler = VNImageRequestHandler.init(cgImage: (imageView.image?.cgImage!)!, orientation: CGImagePropertyOrientation.up)
        let request = reqReq()
        
        DispatchQueue.global(qos: .userInteractive).async {
            do {
                try handler.perform([request])
            }
            catch {
                print("e")
            }
        }
        
    }

    
    func reqReq() -> VNDetectFaceRectanglesRequest {
        let request = VNDetectFaceRectanglesRequest(completionHandler: { (request, error) in
            
            DispatchQueue.main.async {
                
                if let result = request.results {
                    
                    let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -self.imageView!.frame.size.height)
                    let translate = CGAffineTransform.identity.scaledBy(x: self.imageView!.frame.size.width, y: self.imageView!.frame.size.height)
                    
                    //遍歷所有識(shí)別結(jié)果
                    for item in result {
                        
                        //標(biāo)注框
                        let faceRect = UIView(frame: CGRect.zero)
                        faceRect.layer.borderWidth = 3
                        faceRect.layer.borderColor = UIColor.red.cgColor
                        faceRect.backgroundColor = UIColor.clear
                        
                        self.imageView!.addSubview(faceRect)
                        
                        if let faceObservation = item as? VNFaceObservation {
                            
                            let finalRect = faceObservation.boundingBox.applying(translate).applying(transform)
                            faceRect.frame = finalRect
                            
                        }
                        
                    }
                    
                }
            }
            
            
        })
        
        return request
    }

運(yùn)行APP,效果如下:

OpenCV

OpenCV (Open Source Computer Vision Library)是一個(gè)在BSD許可下發(fā)布的開源庫,因此它是免費(fèi)提供給學(xué)術(shù)和商業(yè)用途。有C++、C、Python和Java接口,支持Windows、Linux、MacOS、iOS和Android等系統(tǒng)。OpenCV是為計(jì)算效率而設(shè)計(jì)的,而且密切關(guān)注實(shí)時(shí)應(yīng)用程序的發(fā)展和支持。該庫用優(yōu)化的C/C++編寫,可以應(yīng)用于多核處理。在啟用OpenCL的基礎(chǔ)上,它可以利用底層的異構(gòu)計(jì)算平臺(tái)的硬件加速。

OpenCV 起始于 1999 年 Intel 的一個(gè)內(nèi)部研究項(xiàng)目。從那時(shí)起,它的開發(fā)就一直很活躍。進(jìn)化到現(xiàn)在,它已支持如 OpenCL 和 OpenGL 等現(xiàn)代技術(shù),也支持如 iOS 和 Android 等平臺(tái)。

下面是在官方文檔中列出的最重要的模塊:

  • core:簡潔的核心模塊,定義了基本的數(shù)據(jù)結(jié)構(gòu),包括稠密多維數(shù)組 Mat 和其他模塊需要的基本函數(shù)。
  • imgproc:圖像處理模塊,包括線性和非線性圖像濾波、幾何圖像轉(zhuǎn)換 (縮放、仿射與透視變換、一般性基于表的重映射)、顏色空間轉(zhuǎn)換、直方圖等等。
  • video:視頻分析模塊,包括運(yùn)動(dòng)估計(jì)、背景消除、物體跟蹤算法。
  • calib3d:包括基本的多視角幾何算法、單體和立體相機(jī)的標(biāo)定、對(duì)象姿態(tài)估計(jì)、雙目立體匹配算法和元素的三維重建。
  • features2d:包含了顯著特征檢測算法、描述算子和算子匹配算法。
  • objdetect:物體檢測和一些預(yù)定義的物體的檢測 (如人臉、眼睛、杯子、人、汽車等)。
  • ml:多種機(jī)器學(xué)習(xí)算法,如 K 均值、支持向量機(jī)和神經(jīng)網(wǎng)絡(luò)。
  • highgui:一個(gè)簡單易用的接口,提供視頻捕捉、圖像和視頻編碼等功能,還有簡單的 UI 接口 (iOS 上可用的僅是其一個(gè)子集)。
  • gpu:OpenCV 中不同模塊的 GPU 加速算法 (iOS 上不可用)。
  • ocl:使用 OpenCL 實(shí)現(xiàn)的通用算法 (iOS 上不可用)。
  • 一些其它輔助模塊,如 Python 綁定和用戶貢獻(xiàn)的算法。

cv::Mat 是 OpenCV 的核心數(shù)據(jù)結(jié)構(gòu),用來表示任意 N 維矩陣。因?yàn)閳D像只是 2 維矩陣的一個(gè)特殊場景,所以也是使用 cv::Mat 來表示的。也就是說,cv::Mat 將是你在 OpenCV 中用到最多的類。

如前面所說,OpenCV 是一個(gè) C++ 的 API,因此不能直接在 Swift 和 Objective-C 代碼中使用,但能在 Objective-C++ 文件中使用。

Objective-C++ 是 Objective-C 和 C++ 的混合物,讓你可以在 Objective-C 類中使用 C++ 對(duì)象。clang 編譯器會(huì)把所有后綴名為 .mm 的文件都當(dāng)做是 Objective-C++。一般來說,它會(huì)如你所期望的那樣運(yùn)行,但還是有一些使用 Objective-C++ 的注意事項(xiàng)。內(nèi)存管理是你最應(yīng)該格外注意的點(diǎn),因?yàn)?ARC 只對(duì) Objective-C 對(duì)象有效。當(dāng)你使用一個(gè) C++ 對(duì)象作為類屬性的時(shí)候,其唯一有效的屬性就是 assign。因此,你的 dealloc 函數(shù)應(yīng)確保 C++ 對(duì)象被正確地釋放了。

第二重要的點(diǎn)就是,如果你在 Objective-C++ 頭文件中引入了 C++ 頭文件,當(dāng)你在工程中使用該 Objective-C++ 文件的時(shí)候就泄露了 C++ 的依賴。任何引入你的 Objective-C++ 類的 Objective-C 類也會(huì)引入該 C++ 類,因此該 Objective-C 文件也要被聲明為 Objective-C++ 的文件。這會(huì)像森林大火一樣在工程中迅速蔓延。所以,應(yīng)該把你引入 C++ 文件的地方都用 #ifdef __cplusplus 包起來,并且只要可能,就盡量只在 .mm實(shí)現(xiàn)文件中引入 C++ 頭文件。

要獲得更多如何混用 C++ 和 Objective-C 的細(xì)節(jié),請(qǐng)查看 Matt Galloway 寫的這篇教程

基于OpenCV,iOS應(yīng)用程序可以實(shí)現(xiàn)很多有趣的功能,也可以把很多復(fù)雜的工作簡單化。一般可用于:

  • 對(duì)圖片進(jìn)行灰度處理
  • 人臉識(shí)別,即特征跟蹤
  • 訓(xùn)練圖片特征庫(可用于模式識(shí)別)
  • 提取特定圖像內(nèi)容(根據(jù)需求還原有用圖像信息)

代碼實(shí)現(xiàn)如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //預(yù)設(shè)置face探測的參數(shù)
    [self preSetFace];
    
    //image轉(zhuǎn)mat
    cv::Mat mat;
    UIImageToMat(self.imageView.image, mat);
    
    //執(zhí)行face
    [self processImage:mat];
}

- (void)preSetFace {
    NSString *faceCascadePath = [[NSBundle mainBundle] pathForResource:@"haarcascade_frontalface_alt2"
                                                                ofType:@"xml"];
    
    const CFIndex CASCADE_NAME_LEN = 2048;
    char *CASCADE_NAME = (char *) malloc(CASCADE_NAME_LEN);
    CFStringGetFileSystemRepresentation( (CFStringRef)faceCascadePath, CASCADE_NAME, CASCADE_NAME_LEN);
    
    _faceDetector.load(CASCADE_NAME);
    
    free(CASCADE_NAME);
}

- (void)processImage:(cv::Mat&)inputImage {
    // Do some OpenCV stuff with the image
    cv::Mat frame_gray;

    //轉(zhuǎn)換為灰度圖像
    cv::cvtColor(inputImage, frame_gray, CV_BGR2GRAY);
    
    //圖像均衡化
    cv::equalizeHist(frame_gray, frame_gray);

    //分類器識(shí)別
    _faceDetector.detectMultiScale(frame_gray, _faceRects,1.1,2,0,cv::Size(30,30));

    vector<cv::Rect> faces;
    faces = _faceRects;
    
    // 在每個(gè)人臉上畫一個(gè)紅色四方形
    for(unsigned int i= 0;i < faces.size();i++)
    {
        const cv::Rect& face = faces[i];
        cv::Point tl(face.x,face.y);
        cv::Point br = tl + cv::Point(face.width,face.height);
        // 四方形的畫法
        cv::Scalar magenta = cv::Scalar(255, 0, 0, 255);
        cv::rectangle(inputImage, tl, br, magenta, 3, 8, 0);
    }
    UIImage *outputImage = MatToUIImage(inputImage);
    self.imageView.image = outputImage;
}

運(yùn)行APP,效果如下:

AVFoundation

AVFoundation照片和視頻捕捉功能是從框架搭建之初就是它的強(qiáng)項(xiàng)。 從iOS 4.0 我們就可以直接訪問iOS的攝像頭和攝像頭生成的數(shù)據(jù)(照片、視頻)。目前捕捉功能仍然是蘋果公司媒體工程師最關(guān)注的領(lǐng)域。

AVFoundation實(shí)現(xiàn)視頻捕捉的步驟如下:

  • 捕捉設(shè)備

AVCaptureDevice為攝像頭、麥克風(fēng)等物理設(shè)備提供接口。大部分我們使用的設(shè)備都是內(nèi)置于MAC或者iPhone、iPad上的。當(dāng)然也可能出現(xiàn)外部設(shè)備。但是AVCaptureDevice 針對(duì)物理設(shè)備提供了大量的控制方法。比如控制攝像頭聚焦、曝光、白平衡、閃光燈等。

  • 捕捉設(shè)備的輸入

注意:為捕捉設(shè)備添加輸入,不能添加到AVCaptureSession 中,必須通過將它封裝到一個(gè)AVCaptureDeviceInputs實(shí)例中。這個(gè)對(duì)象在設(shè)備輸出數(shù)據(jù)和捕捉會(huì)話間扮演接線板的作用。

  • 捕捉設(shè)備的輸出

AVCaptureOutput 是一個(gè)抽象類。用于為捕捉會(huì)話得到的數(shù)據(jù)尋找輸出的目的地。框架定義了一些抽象類的高級(jí)擴(kuò)展類。例如 AVCaptureStillImageOutput 和 AVCaptureMovieFileOutput類。使用它們來捕捉靜態(tài)照片、視頻。例如 AVCaptureAudioDataOutput 和 AVCaptureVideoDataOutput ,使用它們來直接訪問硬件捕捉到的數(shù)字樣本。

  • 捕捉連接

AVCaptureConnection類.捕捉會(huì)話先確定由給定捕捉設(shè)備輸入渲染的媒體類型,并自動(dòng)建立其到能夠接收該媒體類型的捕捉輸出端的連接。

  • 捕捉預(yù)覽

如果不能在影像捕捉中看到正在捕捉的場景,那么應(yīng)用程序用戶體驗(yàn)就會(huì)很差。幸運(yùn)的是框架定義了AVCaptureVideoPreviewLayer 類來滿足該需求。這樣就可以對(duì)捕捉的數(shù)據(jù)進(jìn)行實(shí)時(shí)預(yù)覽。


通過一個(gè)特定的AVCaptureOutput類型的AVCaptureMetadataOutput可以實(shí)現(xiàn)人臉檢測功能.支持硬件加速以及同時(shí)對(duì)10個(gè)人臉進(jìn)行實(shí)時(shí)檢測.

當(dāng)使用人臉檢測時(shí),會(huì)輸出一個(gè)具體的子類類型AVMetadataFaceObject,該類型定義了多個(gè)用于描述被檢測到的人臉的屬性,包括人臉的邊界(設(shè)備坐標(biāo)系),以及斜傾角(roll angle,表示人頭部向肩膀方向的側(cè)傾角度)和偏轉(zhuǎn)角(yaw angle,表示人臉繞Y軸旋轉(zhuǎn)的角度).

實(shí)現(xiàn)代碼如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _facesViewArr = [NSMutableArray arrayWithCapacity:0];
    
    //1.獲取輸入設(shè)備(攝像頭)
    NSArray *devices = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack].devices;
    AVCaptureDevice *deviceF = devices[0];
    
//    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
//    AVCaptureDevice *deviceF;
//    for (AVCaptureDevice *device in devices )
//    {
//        if ( device.position == AVCaptureDevicePositionFront )
//        {
//            deviceF = device;
//            break;
//        }
//    }
    
    //2.根據(jù)輸入設(shè)備創(chuàng)建輸入對(duì)象
    AVCaptureDeviceInput*input = [[AVCaptureDeviceInput alloc] initWithDevice:deviceF error:nil];
    
    //3.創(chuàng)建原數(shù)據(jù)的輸出對(duì)象
    AVCaptureMetadataOutput *metaout = [[AVCaptureMetadataOutput alloc] init];
    
    //4.設(shè)置代理監(jiān)聽輸出對(duì)象輸出的數(shù)據(jù),在主線程中刷新
    [metaout setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    self.session = [[AVCaptureSession alloc] init];
    
    //5.設(shè)置輸出質(zhì)量(高像素輸出)
    if ([self.session canSetSessionPreset:AVCaptureSessionPreset640x480]) {
        [self.session setSessionPreset:AVCaptureSessionPreset640x480];
    }
    
    //6.添加輸入和輸出到會(huì)話
    [self.session beginConfiguration];
    if ([self.session canAddInput:input]) {
        [self.session addInput:input];
    }
    if ([self.session canAddOutput:metaout]) {
        [self.session addOutput:metaout];
    }
    [self.session commitConfiguration];
    
    //7.告訴輸出對(duì)象要輸出什么樣的數(shù)據(jù),識(shí)別人臉, 最多可識(shí)別10張人臉
    [metaout setMetadataObjectTypes:@[AVMetadataObjectTypeFace]];
    
    AVCaptureSession *session = (AVCaptureSession *)self.session;
    
    //8.創(chuàng)建預(yù)覽圖層
    _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    _previewLayer.frame = self.view.bounds;
    [self.view.layer insertSublayer:_previewLayer atIndex:0];
    
    //9.設(shè)置有效掃描區(qū)域(默認(rèn)整個(gè)屏幕區(qū)域)(每個(gè)取值0~1, 以屏幕右上角為坐標(biāo)原點(diǎn))
    metaout.rectOfInterest = self.view.bounds;
    
    //前置攝像頭一定要設(shè)置一下 要不然畫面是鏡像
    for (AVCaptureVideoDataOutput* output in session.outputs) {
        for (AVCaptureConnection * av in output.connections) {
            //判斷是否是前置攝像頭狀態(tài)
            if (av.supportsVideoMirroring) {
                //鏡像設(shè)置
                av.videoOrientation = AVCaptureVideoOrientationPortrait;
//                av.videoMirrored = YES;
            }
        }
    }
    
    //10. 開始掃描
    [self.session startRunning];
    
}

- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    //當(dāng)檢測到了人臉會(huì)走這個(gè)回調(diào)
    
    //干掉舊畫框
    for (UIView *faceView in self.facesViewArr) {
        [faceView removeFromSuperview];
    }
    [self.facesViewArr removeAllObjects];
    
    //轉(zhuǎn)換
    for (AVMetadataFaceObject *faceobject in metadataObjects) {
        AVMetadataObject *face = [self.previewLayer transformedMetadataObjectForMetadataObject:faceobject];
        CGRect r = face.bounds;
        //畫框
        UIView *faceBox = [[UIView alloc] initWithFrame:r];
        faceBox.layer.borderWidth = 3;
        faceBox.layer.borderColor = [UIColor redColor].CGColor;
        faceBox.backgroundColor = [UIColor clearColor];
        [self.view addSubview:faceBox];
        [self.facesViewArr addObject:faceBox];
    }

}

運(yùn)行APP,效果如下:

本文四種方式實(shí)現(xiàn)的代碼已放到Github,有需要的可以下載查看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明AGI閱讀 16,003評(píng)論 3 119
  • 夲圣閱讀 192評(píng)論 2 3
  • 要交當(dāng)堂論文,古文學(xué)老師在黑板上刷刷寫下一行字:“莫避春陰上馬遲,春來未有不陰時(shí)。”她說:“味!”辛棄疾老人家的意...
    冷小山閱讀 626評(píng)論 0 0
  • 周末,在朋友家小住, 覺得簡單但很滿足,于是對(duì)朋友說:“在你這里住得不想走了,有好電影、好音樂、好書,還有一張便于...
    微笑的秧秧閱讀 1,341評(píng)論 11 43
  • 【同讀一本書劉姣】 2015-01-17-027 —————— —————《白銀谷》 【正文】 “康笏南何等老辣,...
    城市格調(diào)劉姣閱讀 193評(píng)論 6 0