1"use strict";(globalThis.webpackChunkazurecertprep=globalThis.webpackChunkazurecertprep||[]).push([[704],{36859(e,n,t){t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>d,default:()=>m,frontMatter:()=>l,metadata:()=>i,toc:()=>h});const i=JSON.parse('{"id":"ai-103/computer-vision/challenge-26","title":"Challenge 26: Custom Vision - Object Detection","description":"60 min | Cost Computer Vision Solutions (10-15%)","source":"@site/docs/ai-103/03-computer-vision/challenge-26.md","sourceDirName":"ai-103/03-computer-vision","slug":"/ai-103/computer-vision/challenge-26","permalink":"/docs/ai-103/computer-vision/challenge-26","draft":false,"unlisted":false,"editUrl":"https://github.com/azurecertprep/azurecertprep.github.io/tree/main/docs/ai-103/03-computer-vision/challenge-26.md","tags":[],"version":"current","sidebarPosition":4,"frontMatter":{"sidebar_position":4,"title":"Challenge 26: Custom Vision - Object Detection"},"sidebar":"ai103Sidebar","previous":{"title":"Challenge 25: Custom Vision - Image Classification","permalink":"/docs/ai-103/computer-vision/challenge-25"},"next":{"title":"Challenge 27: OCR - Extract Text from Images","permalink":"/docs/ai-103/computer-vision/challenge-27"}}');var r=t(74848),o=t(28453),s=t(67970),a=t(4865),c=t(19365);const l={sidebar_position:4,title:"Challenge 26: Custom Vision - Object Detection"},d="Challenge 26: Custom Vision - Object Detection",u={},h=[{value:"Exam skills covered",id:"exam-skills-covered",level:2},{value:"Overview",id:"overview",level:2},{value:"Prerequisites",id:"prerequisites",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Task 1: Create Object Detection Project",id:"task-1-create-object-detection-project",level:3},{value:"Task 2: Upload Images with Bounding Box Regions",id:"task-2-upload-images-with-bounding-box-regions",level:3},{value:"Task 3: Train and Evaluate Object Detection Model",id:"task-3-train-and-evaluate-object-detection-model",level:3},{value:"Task 4: Run Object Detection Predictions",id:"task-4-run-object-detection-predictions",level:3},{value:"Expected Output",id:"expected-output",level:2},{value:"Break & fix",id:"break--fix",level:2},{value:"Knowledge Check",id:"knowledge-check",level:2},{value:"Cleanup",id:"cleanup",level:2},{value:"Learn More",id:"learn-more",level:2}];function p(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"challenge-26-custom-vision---object-detection",children:"Challenge 26: Custom Vision - Object Detection"})}),"\n",(0,r.jsx)(n.admonition,{title:"Estimated Time",type:"info",children:(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"60 min"})," | ",(0,r.jsx)(n.strong,{children:"Cost"}),": $2-5 (estimated) | ",(0,r.jsx)(n.strong,{children:"Domain"}),": Computer Vision Solutions (10-15%)"]})}),"\n",(0,r.jsx)(n.h2,{id:"exam-skills-covered",children:"Exam skills covered"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Train custom image model for object detection"}),"\n",(0,r.jsx)(n.li,{children:"Label images with bounding box regions"}),"\n",(0,r.jsx)(n.li,{children:"Evaluate detection metrics (mAP)"}),"\n",(0,r.jsx)(n.li,{children:"Publish and consume object detection model"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsx)(n.p,{children:'Object detection locates and classifies multiple objects within an image using bounding boxes. Unlike classification (which answers "what is this image?"), detection answers "what objects are here and where?"'}),"\n",(0,r.jsx)(n.p,{children:"Key concepts:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Bounding box"}),": Rectangle defined by (left, top, width, height) as normalized coordinates (0.0\u20131.0)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"IoU (Intersection over Union)"}),": Measures overlap between predicted and actual bounding boxes"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"mAP (mean Average Precision)"}),": Primary metric averaging AP across all object classes"]}
1),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Azure subscription"}),"\n",(0,r.jsx)(n.li,{children:"Custom Vision Training + Prediction resources"}),"\n",(0,r.jsx)(n.li,{children:"Python 3.9+"}),"\n",(0,r.jsxs)(n.li,{children:["Package: ",(0,r.jsx)(n.code,{children:"azure-cognitiveservices-vision-customvision"})]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"implementation",children:"Implementation"}),"\n",(0,r.jsx)(n.h3,{id:"task-1-create-object-detection-project",children:"Task 1: Create Object Detection Project"}),"\n",(0,r.jsx)(a.A,{children:(0,r.jsx)(c.A,{value:"python",label:"Python SDK",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'import os\nimport time\nfrom azure.cognitiveservices.vision.customvision.training import CustomVisionTrainingClient\nfrom azure.cognitiveservices.vision.customvision.training.models import (\n ImageUrlCreateEntry, Region\n)\nfrom azure.cognitiveservices.vision.customvision.prediction import CustomVisionPredictionClient\nfrom msrest.authentication import ApiKeyCredentials\n\ntraining_key = os.environ["CUSTOM_VISION_TRAINING_KEY"]\ntraining_endpoint = os.environ["CUSTOM_VISION_TRAINING_ENDPOINT"]\n\ncredentials = ApiKeyCredentials(in_headers={"Training-key": training_key})\ntrainer = CustomVisionTrainingClient(training_endpoint, credentials)\n\n# Find the Object Detection domain\ndomains = trainer.get_domains()\nobj_detection_domain = next(d for d in domains if d.type == "ObjectDetection" and not d.exportable)\nprint(f"Domain: {obj_detection_domain.name} ({obj_detection_domain.id})")\n\n# Create object detection project\nproject = trainer.create_project(\n name="Vehicle-Detector",\n domain_id=obj_detection_domain.id\n)\nprint(f"Created project: {project.name} ({project.id})")\n\n# Create tags for objects to detect\ncar_tag = trainer.create_tag(project.id, "car")\ntruck_tag = trainer.create_tag(project.id, "truck")\nbicycle_tag = trainer.create_tag(project.id, "bicycle")\nprint(f"Tags: car={car_tag.id}, truck={truck_tag.id}, bicycle={bicycle_tag.id}")\n'})})})}),"\n",(0,r.jsx)(n.h3,{id:"task-2-upload-images-with-bounding-box-regions",children:"Task 2: Upload Images with Bounding Box Regions"}),"\n",(0,r.jsx)(a.A,{children:(0,r.jsx)(c.A,{value:"python",label:"Python SDK",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'# Regions use normalized coordinates (0.0 to 1.0 relative to image dimensions)\n# Format: Region(tag_id, left, top, width, height)\n\ntraining_images = [\n {\n "url": "https://example.com/traffic1.jpg",\n "regions": [\n Region(tag_id=car_tag.id, left=0.1, top=0.3, width=0.25, height=0.2),\n Region(tag_id=car_tag.id, left=0.5, top=0.35, width=0.2, height=0.18),\n Region(tag_id=truck_tag.id, left=0.7, top=0.2, width=0.28, height=0.3),\n ]\n },\n {\n "url": "https://example.com/traffic2.jpg",\n "regions": [\n Region(tag_id=bicycle_tag.id, left=0.05, top=0.4, width=0.15, height=0.25),\n Region(tag_id=car_tag.id, left=0.4, top=0.3, width=0.3, height=0.22),\n ]\n }\n]\n\n# Upload images with regions\nimage_entries = []\nfor img in training_images:\n entry = ImageUrlCreateEntry(\n url=img["url"],\n regions=img["regions"]\n )\n image_entries.append(entry)\n\nupload_result = trainer.create_images_from_urls(\n project.id,\n images=image_entr
1ies\n)\nprint(f"Upload success: {upload_result.is_batch_successful}")\nfor image in upload_result.images:\n print(f" {image.source_url}: {image.status}")\n'})})})}),"\n",(0,r.jsx)(n.h3,{id:"task-3-train-and-evaluate-object-detection-model",children:"Task 3: Train and Evaluate Object Detection Model"}),"\n",(0,r.jsx)(a.A,{children:(0,r.jsx)(c.A,{value:"python",label:"Python SDK",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'# Train the model\nprint("Training object detection model...")\niteration = trainer.train_project(project.id)\n\nwhile iteration.status != "Completed":\n iteration = trainer.get_iteration(project.id, iteration.id)\n print(f" Status: {iteration.status}")\n time.sleep(10)\n\nprint(f"Training complete: {iteration.id}")\n\n# Evaluate performance\nperformance = trainer.get_iteration_performance(project.id, iteration.id)\nprint(f"\\nDetection Metrics:")\nprint(f" Precision: {performance.precision:.4f}")\nprint(f" Recall: {performance.recall:.4f}")\nprint(f" mAP: {performance.average_precision:.4f}")\n\nfor tag_perf in performance.per_tag_performance:\n print(f" \'{tag_perf.name}\': precision={tag_perf.precision:.3f}, recall={tag_perf.recall:.3f}, AP={tag_perf.average_precision:.3f}")\n\n# Publish\nprediction_resource_id = "/subscriptions/<sub-id>/resourceGroups/rg-ai102-customvision/providers/Microsoft.CognitiveServices/accounts/cv-prediction-ai102"\npublish_name = "vehicle-detector-v1"\n\ntrainer.publish_iteration(project.id, iteration.id, publish_name, prediction_resource_id)\nprint(f"\\nPublished as: {publish_name}")\n'})})})}),"\n",(0,r.jsx)(n.h3,{id:"task-4-run-object-detection-predictions",children:"Task 4: Run Object Detection Predictions"}),"\n",(0,r.jsxs)(a.A,{children:[(0,r.jsx)(c.A,{value:"python",label:"Python SDK",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'prediction_key = os.environ["CUSTOM_VISION_PREDICTION_KEY"]\nprediction_endpoint = os.environ["CUSTOM_VISION_PREDICTION_ENDPOINT"]\n\npred_credentials = ApiKeyCredentials(in_headers={"Prediction-key": prediction_key})\npredictor = CustomVisionPredictionClient(prediction_endpoint, pred_credentials)\n\n# Detect objects in a new image\ntest_url = "https://example.com/street-scene.jpg"\nresults = predictor.detect_image_url(project.id, publish_name, url=test_url)\n\nprint(f"\\nDetection Results:")\nprint(f"Objects found: {len(results.predictions)}")\n\nfor detection in results.predictions:\n if detection.probability > 0.5: # Confidence threshold\n bbox = detection.bounding_box\n print(f" {detection.tag_name} ({detection.probability:.1%})")\n print(f" Box: left={bbox.left:.3f}, top={bbox.top:.3f}, "\n f"width={bbox.width:.3f}, height={bbox.height:.3f}")\n\n# Convert normalized to pixel coordinates (for a 1920x1080 image)\nimage_width, image_height = 1920, 1080\nfor detection in results.predictions:\n if detection.probability > 0.5:\n bbox = detection.bounding_box\n pixel_left = int(bbox.left * image_width)\n pixel_top = int(bbox.top * image_height)\n pixel_width = int(bbox.width * image_width)\n pixel_height = int(bbox.height * image_height)\n print(f" {detection.tag_name}: ({pixel_left}, {pixel_top}) -> ({pixel_left+pixel_width}, {pixel_top+pixel_height})")\n'})})}),(0,r.jsx)(c.A,{value:"rest",label:"REST API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'PREDICTION_ENDPOINT="https://<resource>.cognitiveservices.azure.com"\nPREDICTION_KEY="<key>"\nPROJECT_ID="<project-id>"\n\ncurl -s "${PREDICTION_ENDPOINT}/customvision/v3.0/prediction/${PROJECT_ID}/detect/iterations/vehicle-detector-v1/url" \\\n -H "Prediction-Key: ${PREDICTION_KEY}" \\\n -H "Content-Type: application/json" \\\n -d \'{"url": "https://example.com/street-scene.jpg"}\' \\\n | jq \'.predictions[] | select(.probability > 0.5) | {tag: .tagName, probability: .probability, boundingBox}\'\n'})})})]}),"\n",(0,r.jsx)(n.h2,{id:"expected-output",children:"Expected Output"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-text",children:"Domain: General (Object Detection)\nCreated project: Vehicle-Detector\nTags: car=..., truck=..., bicycle=...\nUpload success: True\
1n\nTraining object detection model...\n Status: Training\n Status: Completed\nTraining complete: iter-67890\n\nDetection Metrics:\n Precision: 0.8850\n Recall: 0.8200\n mAP: 0.8734\n 'car': precision=0.920, recall=0.880, AP=0.910\n 'truck': precision=0.870, recall=0.790, AP=0.850\n 'bicycle': precision=0.865, recall=0.770, AP=0.860\n\nPublished as: vehicle-detector-v1\n\nDetection Results:\nObjects found: 4\n car (95.2%)\n Box: left=0.102, top=0.298, width=0.245, height=0.198\n car (87.3%)\n Box: left=0.510, top=0.320, width=0.190, height=0.175\n truck (82.1%)\n Box: left=0.720, top=0.180, width=0.260, height=0.310\n"})}),"\n",(0,r.jsx)(n.h2,{id:"break--fix",children:"Break & fix"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Scenario"}),(0,r.jsx)(n.th,{children:"Symptom"}),(0,r.jsx)(n.th,{children:"Root Cause"}),(0,r.jsx)(n.th,{children:"Fix"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Regions rejected"}),(0,r.jsx)(n.td,{children:"Invalid region coordinates"}),(0,r.jsx)(n.td,{children:"Coordinates outside 0.0\u20131.0 range"}),(0,r.jsx)(n.td,{children:"Normalize: left+width \u2264 1.0, top+height \u2264 1.0"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Low mAP"}),(0,r.jsx)(n.td,{children:"Poor detection accuracy"}),(0,r.jsx)(n.td,{children:"Inconsistent bounding box labeling"}),(0,r.jsx)(n.td,{children:"Re-label with tight, consistent boxes; more training data"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Overlapping detections"}),(0,r.jsx)(n.td,{children:"Duplicate predictions"}),(0,r.jsx)(n.td,{children:"No NMS threshold configured"}),(0,r.jsx)(n.td,{children:"Apply confidence threshold; use Non-Maximum Suppression"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Training fails"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"BadRequestImageRegions"})}),(0,r.jsx)(n.td,{children:"Regions too small or missing"}),(0,r.jsx)(n.td,{children:"Minimum region size ~5% of image area"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Wrong endpoint"}),(0,r.jsx)(n.td,{children:"404 on detection"}),(0,r.jsx)(n.td,{children:"Using classify endpoint for detection"}),(0,r.jsxs)(n.td,{children:["Use ",(0,r.jsx)(n.code,{children:"/detect/"})," not ",(0,r.jsx)(n.code,{children:"/classify/"})," in prediction URL"]})]})]})]}),"\n",(0,r.jsx)(n.h2,{id:"knowledge-check",children:"Knowledge Check"}),"\n",(0,r.jsx)(s.A,{questions:[{question:"How are bounding box coordinates represented in Custom Vision object detection?",options:["Normalized coordinates from 0.0 to 1.0 relative to image dimensions (left, top, width, height)","Absolute pixel coordinates (x, y, width, height)","Percentage values from 0% to 100%","Center point plus radius"],correctAnswer:0,explanation:"Custom Vision uses normalized coordinates (0.0 to 1.0) for bounding boxes: left, top, width, height \u2014 all relative to image dimensions. This makes them resolution-independent."},{question:"What does mAP (mean Average Precision) measure in object detection?",options:["The average confidence score across all detections","The maximum number of objects detected per image","The average processing time per image","The mean of Average Precision computed independently for each object class"],correctAnswer:3,explanation:"mAP is the mean of AP values calculated separately for each class, providing a single metric that summarizes detection performance across all categories."},{question:"What is the key difference between the classify and detect prediction endpoints?",options:["Classify is faster than detect","Detect requires more training images","Classify returns a single tag; detect returns multiple tags with bounding box coordinates","Classify works with URLs; detect only works with local files"],correctAnswer:2,explanation:"The classify endpoint returns tags for the whole image. The detect endpoint returns multiple object predictions, each with a tag AND bounding box coordinates."},{question:"What is IoU (Intersection over Union) used for?",options:["Measuring how much training data overlaps between classes","Measuring the overlap between a predicted bounding box and ground-truth box to determine if a detection is correct","Calculating the ratio of input to output images","Determining how many users are accessing the model simultaneously"],correctAnswer:1,explanation:"IoU measures the overlap between predicted and ground-truth bounding boxes. A detection is typically considered correct if IoU > 0.5 (50% overlap with the actual object)."},{question:"When labeling training images for object detection, what coordinates do you need for each object?",options:["Only the center point of the object","The pixel coordinates of all four corners","A polygon outline of the object shape","Left, top, width, and height of the bounding box (normalized 0.0\u20131.0) plus the tag ID"],correctAnswer:3,explanation:"Each region requires: tag_id (which object class), left, top, width, height \u2014 all as normalized values between 0.0 and 1.0 relative to image dimensions."}]}),"\n",(0,r.jsx)(n.h2,{id:"cleanup",children:"Cleanup"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"az group delete --name rg-ai102-customvision --yes --no-wait\n"})}),"\n",(0,r.jsx)(n.h2,{id:"learn-more",children:"Learn More"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"https://learn.microsoft.com/azure/ai-services/custom-vision-service/get-started-build-detector",children:"Object detection overview"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"https://learn.microsoft.com/azure/ai-services/custom-vision-service/getting-started-build-a-classifier",children:"How to label images"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"https://learn.microsoft.com/rest/api/customvision/prediction",children:"Prediction API reference"})}),"\n"]})]})}function m(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(p,{...e})}):p(e)}},19365(e,n,t){t.d(n,{A:()=>c});t(96540);var i=t(34164),r=t(47751);const o="tabItem_Ymn6";var s=t(74848);function a({children:e,className:n,hidden:t}){return(0,s.jsx)("div",{role:"tabpanel",className:(0,i.A)(o,n),hidden:t,children:e})}function c({children:e,className:n,value:t}){const{selectedValue:i,lazy:o}=(0,r.uc)(),c=t===i;return!c&&o?null:(0,s.jsx)(a,{className:n,hidden:!c,children:e})}},4865(e,n,t){t.d(n,{A:()=>m});t(96540);var i=t(34164),r=t(17559),o=t(47751),s=t(23104),a=t(92303);const c="tabList__CuJ",l="tabItem_LNqP";var d=t(74848);function u({className:e}){const{selectedValue:n,selectValue:t,tabValues:r,block:a}=(0,o.uc)(),c=[],{blockElementScrollPositionUntilNextRender:u}
1=(0,s.a_)(),h=e=>{const i=e.currentTarget,o=c.indexOf(i),s=r[o].value;s!==n&&(u(i),t(s))},p=e=>{let n=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,d.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":a},e),children:r.map(({value:e,label:t,attributes:r})=>(0,d.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{c.push(e)},onKeyDown:p,onClick:h,...r,className:(0,i.A)("tabs__item",l,r?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function h({children:e}){return(0,d.jsx)("div",{className:"margin-top--md",children:e})}function p({className:e,children:n}){return(0,d.jsxs)("div",{className:(0,i.A)(r.G.tabs.container,"tabs-container",c),children:[(0,d.jsx)(u,{className:e}),(0,d.jsx)(h,{children:n})]})}function m(e){const n=(0,a.A)(),t=(0,o.OC)(e);return(0,d.jsx)(o.O_,{value:t,children:(0,d.jsx)(p,{className:e.className,children:(0,o.vT)(e.children)})},String(n))}},47751(e,n,t){t.d(n,{OC:()=>m,O_:()=>x,uc:()=>b,vT:()=>d});var i=t(96540),r=t(56347),o=t(205),s=t(57485),a=t(70679),c=t(31682),l=t(74848);function d(e){return i.Children.toArray(e).filter(e=>"\n"!==e)}function u(e){const{values:n,children:t}=e;return(0,i.useMemo)(()=>{const e=n??function(e){return i.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:i}})=>({value:e,label:n,attributes:t,default:i}))}(t);return function(e){const n=(0,c.XI)(e,(e,n)=>e.value===n.value);if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,t])}function h({value:e,tabValues:n}){return n.some(n=>n.value===e)}function p({queryString:e=!1,groupId:n}){const t=(0,r.W6)(),o=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,s.aZ)(o),(0,i.useCallback)(e=>{if(!o)return;const n=new URLSearchParams(t.location.search);n.set(o,e),t.replace({...t.location,search:n.toString()})},[o,t])]}function m(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,s=u(e),[c,l]=(0,i.useState)(()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!h({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find(e=>e.default)??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:s})),[d,m]=p({queryString:t,groupId:r}),[g,b]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,r]=(0,a.Dv)(n);return[t,(0,i.useCallback)(e=>{n&&r.set(e)},[n,r])]}({groupId:r}),x=(()=>{const e=d??g;return h({value:e,tabValues:s})?e:null})();(0,o.A)(()=>{x&&l(x)},[x]);return{selectedValue:c,selectValue:(0,i.useCallback)(e=>{if(!h({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);l(e),m(e),b(e)},[m,b,s]),tabValues:s,lazy:e.lazy??!1,block:e.block??!1}}const g=(0,i.createContext)(null);function b(){const e=i.useContext(g);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function x(e){return(0,l.jsx)(g.Provider,{value:e.value,children:e.children})}},67970(e,n,t){t.d(n,{A:()=>C});var i=t(96540);const r="container_Wc2S",o="question_w8BQ",s="questionText_khaT",a="questionNumber_tNAi",c="options_hoT3",l="option_h5Gz",d="correct_wiKw",u="incorrect_jk7n",h="dimmed_Rvup",p="optionLetter_SgXF",m="optionText_qkBQ",g="checkmark_PyUX",b="crossmark__CNH",x="explanationCorrect_PaNS",f="explanationIncorrect_Ul4c",j="scoreCard_qRIf",v="perfect_Nu3_",_="good_kzz1",w="review_WPHG";var y=t(74848);function C({questions:e}){const[n,t]=(0,i.useState)({}
1),o=Object.keys(n).length===e.length,s=Object.values(n).filter(Boolean).length,a=(e,n)=>{t(t=>({...t,[e]:n}))};return(0,y.jsxs)("div",{className:r,children:[e.map((e,n)=>(0,y.jsx)(T,{index:n,question:e.question,options:e.options,correctIndex:e.correctAnswer??e.correctIndex,explanation:e.explanation,onAnswer:a},n)),o&&(0,y.jsxs)("div",{className:j,children:[(0,y.jsxs)("strong",{children:["Score: ",s,"/",e.length]}),s===e.length?(0,y.jsx)("span",{className:v,children:" \u2014 Perfect!"}):s>=.75*e.length?(0,y.jsx)("span",{className:_,children:" \u2014 Good job!"}):(0,y.jsx)("span",{className:w,children:" \u2014 Review the explanations above."})]})]})}function T({index:e,question:n,options:t,correctIndex:r,explanation:j,onAnswer:v}){const[_,w]=(0,i.useState)(null),[C,T]=(0,i.useState)(!1);return(0,y.jsxs)("div",{className:o,children:[(0,y.jsxs)("p",{className:s,children:[(0,y.jsxs)("span",{className:a,children:[e+1,"."]})," ",n]}),(0,y.jsx)("div",{className:c,children:t.map((n,t)=>{let i=l;return C&&(i+=t===r?" "+d:t===_?" "+u:" "+h),(0,y.jsxs)("button",{className:i,onClick:()=>(n=>{C||(w(n),T(!0),v(e,n===r))})(t),disabled:C,children:[(0,y.jsx)("span",{className:p,children:String.fromCharCode(65+t)}),(0,y.jsx)("span",{className:m,children:n}),C&&t===r&&(0,y.jsx)("span",{className:g,children:"\u2713"}),C&&t===_&&t!==r&&(0,y.jsx)("span",{className:b,children:"\u2717"})]},t)})}),C&&(0,y.jsxs)("div",{className:_===r?x:f,children:[(0,y.jsx)("strong",{children:_===r?"Correct!":"Incorrect."})," ",j]})]})}},28453(e,n,t){t.d(n,{R:()=>s,x:()=>a});var i=t(96540);const r={},o=i.createContext(r);function s(e){const n=i.useContext(o);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.